PageRenderTime 55ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/Raven.Database/Bundles/Replication/Responders/DocumentReplicationResponder.cs

https://github.com/seanepping/ravendb
C# | 173 lines | 148 code | 18 blank | 7 comment | 17 complexity | 7277ba01637090c7caa84dd15cc2997d MD5 | raw file
Possible License(s): BSD-3-Clause, CC-BY-SA-3.0, MPL-2.0-no-copyleft-exception
  1. //-----------------------------------------------------------------------
  2. // <copyright file="DocumentReplicationResponder.cs" company="Hibernating Rhinos LTD">
  3. // Copyright (c) Hibernating Rhinos LTD. All rights reserved.
  4. // </copyright>
  5. //-----------------------------------------------------------------------
  6. using System;
  7. using System.Collections.Generic;
  8. using System.ComponentModel.Composition;
  9. using System.Linq;
  10. using System.Security.Cryptography;
  11. using System.Text;
  12. using Raven.Abstractions.Logging;
  13. using Raven.Abstractions.Util.Encryptors;
  14. using Raven.Bundles.Replication.Tasks;
  15. using Raven.Database.Server;
  16. using Raven.Imports.Newtonsoft.Json.Linq;
  17. using Raven.Abstractions.Data;
  18. using Raven.Abstractions.Extensions;
  19. using Raven.Bundles.Replication.Data;
  20. using Raven.Bundles.Replication.Plugins;
  21. using Raven.Database.Extensions;
  22. using Raven.Database.Server.Abstractions;
  23. using Raven.Database.Storage;
  24. using Raven.Json.Linq;
  25. namespace Raven.Bundles.Replication.Responders
  26. {
  27. [ExportMetadata("Bundle", "Replication")]
  28. [InheritedExport(typeof(AbstractRequestResponder))]
  29. public class DocumentReplicationResponder : AbstractRequestResponder
  30. {
  31. private readonly ILog log = LogManager.GetCurrentClassLogger();
  32. private ReplicationTask replicationTask;
  33. public ReplicationTask ReplicationTask
  34. {
  35. get { return replicationTask ?? (replicationTask = Database.StartupTasks.OfType<ReplicationTask>().FirstOrDefault()); }
  36. }
  37. [ImportMany]
  38. public IEnumerable<AbstractDocumentReplicationConflictResolver> ReplicationConflictResolvers { get; set; }
  39. public override void Respond(IHttpContext context)
  40. {
  41. var src = context.Request.QueryString["from"];
  42. if (string.IsNullOrEmpty(src))
  43. {
  44. context.SetStatusToBadRequest();
  45. return;
  46. }
  47. while (src.EndsWith("/"))
  48. src = src.Substring(0, src.Length - 1);// remove last /, because that has special meaning for Raven
  49. if (string.IsNullOrEmpty(src))
  50. {
  51. context.SetStatusToBadRequest();
  52. return;
  53. }
  54. var array = context.ReadJsonArray();
  55. if (ReplicationTask != null)
  56. ReplicationTask.HandleHeartbeat(src);
  57. using (Database.DisableAllTriggersForCurrentThread())
  58. {
  59. Database.TransactionalStorage.Batch(actions =>
  60. {
  61. string lastEtag = Etag.Empty.ToString();
  62. foreach (RavenJObject document in array)
  63. {
  64. var metadata = document.Value<RavenJObject>("@metadata");
  65. if (metadata[Constants.RavenReplicationSource] == null)
  66. {
  67. // not sure why, old document from when the user didn't have replication
  68. // that we suddenly decided to replicate, choose the source for that
  69. metadata[Constants.RavenReplicationSource] = RavenJToken.FromObject(src);
  70. }
  71. lastEtag = metadata.Value<string>("@etag");
  72. var id = metadata.Value<string>("@id");
  73. document.Remove("@metadata");
  74. ReplicateDocument(actions, id, metadata, document, src);
  75. }
  76. var replicationDocKey = Constants.RavenReplicationSourcesBasePath + "/" + src;
  77. var replicationDocument = Database.Get(replicationDocKey, null);
  78. var lastAttachmentId = Etag.Empty;
  79. if (replicationDocument != null)
  80. {
  81. lastAttachmentId =
  82. replicationDocument.DataAsJson.JsonDeserialization<SourceReplicationInformation>().
  83. LastAttachmentEtag;
  84. }
  85. Guid serverInstanceId;
  86. if (Guid.TryParse(context.Request.QueryString["dbid"], out serverInstanceId) == false)
  87. serverInstanceId = Database.TransactionalStorage.Id;
  88. Database.Put(replicationDocKey, null,
  89. RavenJObject.FromObject(new SourceReplicationInformation
  90. {
  91. Source = src,
  92. LastDocumentEtag = Etag.Parse(lastEtag),
  93. LastAttachmentEtag = lastAttachmentId,
  94. ServerInstanceId = serverInstanceId
  95. }),
  96. new RavenJObject(), null);
  97. });
  98. }
  99. }
  100. private void ReplicateDocument(IStorageActionsAccessor actions, string id, RavenJObject metadata, RavenJObject document, string src)
  101. {
  102. new DocumentReplicationBehavior
  103. {
  104. Actions = actions,
  105. Database = Database,
  106. ReplicationConflictResolvers = ReplicationConflictResolvers,
  107. Src = src
  108. }.Replicate(id, metadata, document);
  109. }
  110. private static string HashReplicationIdentifier(RavenJObject metadata)
  111. {
  112. using (var md5 = MD5.Create())
  113. {
  114. var bytes =
  115. Encoding.UTF8.GetBytes(metadata.Value<string>(Constants.RavenReplicationSource) + "/" +
  116. metadata.Value<string>("@etag"));
  117. var hash = Encryptor.Current.Hash.Compute16(bytes);
  118. Array.Resize(ref hash, 16);
  119. return new Guid(hash).ToString();
  120. }
  121. }
  122. private string HashReplicationIdentifier(Guid existingEtag)
  123. {
  124. using (var md5 = MD5.Create())
  125. {
  126. var bytes = Encoding.UTF8.GetBytes(Database.TransactionalStorage.Id + "/" + existingEtag);
  127. var hash = Encryptor.Current.Hash.Compute16(bytes);
  128. Array.Resize(ref hash, 16);
  129. return new Guid(hash).ToString();
  130. }
  131. }
  132. private static bool IsDirectChildOfCurrentDocument(JsonDocument existingDoc, RavenJObject metadata)
  133. {
  134. var version = new RavenJObject
  135. {
  136. {Constants.RavenReplicationSource, existingDoc.Metadata[Constants.RavenReplicationSource]},
  137. {Constants.RavenReplicationVersion, existingDoc.Metadata[Constants.RavenReplicationVersion]},
  138. };
  139. var history = metadata[Constants.RavenReplicationHistory];
  140. if (history == null || history.Type == JTokenType.Null) // no history, not a parent
  141. return false;
  142. if (history.Type != JTokenType.Array)
  143. return false;
  144. return history.Values().Contains(version, new RavenJTokenEqualityComparer());
  145. }
  146. public override string UrlPattern
  147. {
  148. get { return "^/replication/replicateDocs$"; }
  149. }
  150. public override string[] SupportedVerbs
  151. {
  152. get { return new[] { "POST" }; }
  153. }
  154. }
  155. }