/Raven.Database/Bundles/Replication/Responders/DocumentReplicationResponder.cs
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
- //-----------------------------------------------------------------------
- // <copyright file="DocumentReplicationResponder.cs" company="Hibernating Rhinos LTD">
- // Copyright (c) Hibernating Rhinos LTD. All rights reserved.
- // </copyright>
- //-----------------------------------------------------------------------
- using System;
- using System.Collections.Generic;
- using System.ComponentModel.Composition;
- using System.Linq;
- using System.Security.Cryptography;
- using System.Text;
- using Raven.Abstractions.Logging;
- using Raven.Abstractions.Util.Encryptors;
- using Raven.Bundles.Replication.Tasks;
- using Raven.Database.Server;
- using Raven.Imports.Newtonsoft.Json.Linq;
- using Raven.Abstractions.Data;
- using Raven.Abstractions.Extensions;
- using Raven.Bundles.Replication.Data;
- using Raven.Bundles.Replication.Plugins;
- using Raven.Database.Extensions;
- using Raven.Database.Server.Abstractions;
- using Raven.Database.Storage;
- using Raven.Json.Linq;
-
- namespace Raven.Bundles.Replication.Responders
- {
- [ExportMetadata("Bundle", "Replication")]
- [InheritedExport(typeof(AbstractRequestResponder))]
- public class DocumentReplicationResponder : AbstractRequestResponder
- {
- private readonly ILog log = LogManager.GetCurrentClassLogger();
- private ReplicationTask replicationTask;
- public ReplicationTask ReplicationTask
- {
- get { return replicationTask ?? (replicationTask = Database.StartupTasks.OfType<ReplicationTask>().FirstOrDefault()); }
- }
-
- [ImportMany]
- public IEnumerable<AbstractDocumentReplicationConflictResolver> ReplicationConflictResolvers { get; set; }
-
-
- public override void Respond(IHttpContext context)
- {
- var src = context.Request.QueryString["from"];
- if (string.IsNullOrEmpty(src))
- {
- context.SetStatusToBadRequest();
- return;
- }
- while (src.EndsWith("/"))
- src = src.Substring(0, src.Length - 1);// remove last /, because that has special meaning for Raven
- if (string.IsNullOrEmpty(src))
- {
- context.SetStatusToBadRequest();
- return;
- }
- var array = context.ReadJsonArray();
- if (ReplicationTask != null)
- ReplicationTask.HandleHeartbeat(src);
- using (Database.DisableAllTriggersForCurrentThread())
- {
- Database.TransactionalStorage.Batch(actions =>
- {
- string lastEtag = Etag.Empty.ToString();
- foreach (RavenJObject document in array)
- {
- var metadata = document.Value<RavenJObject>("@metadata");
- if (metadata[Constants.RavenReplicationSource] == null)
- {
- // not sure why, old document from when the user didn't have replication
- // that we suddenly decided to replicate, choose the source for that
- metadata[Constants.RavenReplicationSource] = RavenJToken.FromObject(src);
- }
- lastEtag = metadata.Value<string>("@etag");
- var id = metadata.Value<string>("@id");
- document.Remove("@metadata");
- ReplicateDocument(actions, id, metadata, document, src);
- }
-
- var replicationDocKey = Constants.RavenReplicationSourcesBasePath + "/" + src;
- var replicationDocument = Database.Get(replicationDocKey, null);
- var lastAttachmentId = Etag.Empty;
- if (replicationDocument != null)
- {
- lastAttachmentId =
- replicationDocument.DataAsJson.JsonDeserialization<SourceReplicationInformation>().
- LastAttachmentEtag;
- }
- Guid serverInstanceId;
- if (Guid.TryParse(context.Request.QueryString["dbid"], out serverInstanceId) == false)
- serverInstanceId = Database.TransactionalStorage.Id;
- Database.Put(replicationDocKey, null,
- RavenJObject.FromObject(new SourceReplicationInformation
- {
- Source = src,
- LastDocumentEtag = Etag.Parse(lastEtag),
- LastAttachmentEtag = lastAttachmentId,
- ServerInstanceId = serverInstanceId
- }),
- new RavenJObject(), null);
- });
- }
- }
-
- private void ReplicateDocument(IStorageActionsAccessor actions, string id, RavenJObject metadata, RavenJObject document, string src)
- {
- new DocumentReplicationBehavior
- {
- Actions = actions,
- Database = Database,
- ReplicationConflictResolvers = ReplicationConflictResolvers,
- Src = src
- }.Replicate(id, metadata, document);
- }
-
- private static string HashReplicationIdentifier(RavenJObject metadata)
- {
- using (var md5 = MD5.Create())
- {
- var bytes =
- Encoding.UTF8.GetBytes(metadata.Value<string>(Constants.RavenReplicationSource) + "/" +
- metadata.Value<string>("@etag"));
-
- var hash = Encryptor.Current.Hash.Compute16(bytes);
- Array.Resize(ref hash, 16);
-
- return new Guid(hash).ToString();
- }
- }
-
- private string HashReplicationIdentifier(Guid existingEtag)
- {
- using (var md5 = MD5.Create())
- {
- var bytes = Encoding.UTF8.GetBytes(Database.TransactionalStorage.Id + "/" + existingEtag);
-
- var hash = Encryptor.Current.Hash.Compute16(bytes);
- Array.Resize(ref hash, 16);
-
- return new Guid(hash).ToString();
- }
- }
-
- private static bool IsDirectChildOfCurrentDocument(JsonDocument existingDoc, RavenJObject metadata)
- {
- var version = new RavenJObject
- {
- {Constants.RavenReplicationSource, existingDoc.Metadata[Constants.RavenReplicationSource]},
- {Constants.RavenReplicationVersion, existingDoc.Metadata[Constants.RavenReplicationVersion]},
- };
-
- var history = metadata[Constants.RavenReplicationHistory];
- if (history == null || history.Type == JTokenType.Null) // no history, not a parent
- return false;
-
- if (history.Type != JTokenType.Array)
- return false;
-
- return history.Values().Contains(version, new RavenJTokenEqualityComparer());
- }
-
- public override string UrlPattern
- {
- get { return "^/replication/replicateDocs$"; }
- }
-
- public override string[] SupportedVerbs
- {
- get { return new[] { "POST" }; }
- }
- }
- }