PageRenderTime 50ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/Raven.Smuggler/Smuggler.cs

http://github.com/ravendb/ravendb
C# | 438 lines | 385 code | 45 blank | 8 comment | 74 complexity | 18ee9d93e7c0cd4db672d25209476692 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, Apache-2.0, BSD-3-Clause
  1. //-----------------------------------------------------------------------
  2. // <copyright file="Smuggler.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.Diagnostics;
  9. using System.IO;
  10. using System.IO.Compression;
  11. using System.Linq;
  12. using System.Net;
  13. using System.Text;
  14. using Newtonsoft.Json;
  15. using Raven.Json.Linq;
  16. namespace Raven.Smuggler
  17. {
  18. public class Smuggler
  19. {
  20. static void Main(string[] args)
  21. {
  22. if (args.Length < 3 || args[0] != "in" && args[0] != "out")
  23. {
  24. Console.WriteLine(@"
  25. Raven Smuggler - Import/Export utility
  26. Usage:
  27. - Import the dump.raven file to a local instance:
  28. Raven.Smuggler in http://localhost:8080/ dump.raven
  29. - Export a local instance to dump.raven:
  30. Raven.Smuggler out http://localhost:8080/ dump.raven
  31. Optional arguments (after required arguments):
  32. --only-indexes : exports only index definitions
  33. --include-attachments : also export attachments
  34. ");
  35. Environment.Exit(-1);
  36. }
  37. try
  38. {
  39. var instanceUrl = args[1];
  40. if (instanceUrl.EndsWith("/") == false)
  41. instanceUrl += "/";
  42. var file = args[2];
  43. switch (args[0])
  44. {
  45. case "in":
  46. ImportData(instanceUrl, file);
  47. break;
  48. case "out":
  49. bool exportIndexesOnly = args.Any(arg => arg.Equals("--only-indexes"));
  50. bool inlcudeAttachments = args.Any(arg => arg.Equals("--include-attachments"));
  51. ExportData(new ExportSpec(instanceUrl, file, exportIndexesOnly, inlcudeAttachments));
  52. break;
  53. }
  54. }
  55. catch (Exception e)
  56. {
  57. Console.WriteLine(e);
  58. Environment.Exit(-1);
  59. }
  60. }
  61. public class ExportSpec
  62. {
  63. public ExportSpec(string instanceUrl, string file, bool exportIndexesOnly, bool includeAttachments)
  64. {
  65. InstanceUrl = instanceUrl;
  66. File = file;
  67. ExportIndexesOnly = exportIndexesOnly;
  68. IncludeAttachments = includeAttachments;
  69. }
  70. public string InstanceUrl { get; private set; }
  71. public string File { get; private set; }
  72. public bool ExportIndexesOnly { get; private set; }
  73. public bool IncludeAttachments { get; private set; }
  74. }
  75. public static void ExportData(ExportSpec exportSpec)
  76. {
  77. using (var streamWriter = new StreamWriter(new GZipStream(File.Create(exportSpec.File), CompressionMode.Compress)))
  78. {
  79. var jsonWriter = new JsonTextWriter(streamWriter)
  80. {
  81. Formatting = Formatting.Indented
  82. };
  83. jsonWriter.WriteStartObject();
  84. jsonWriter.WritePropertyName("Indexes");
  85. jsonWriter.WriteStartArray();
  86. using (var webClient = new WebClient())
  87. {
  88. webClient.UseDefaultCredentials = true;
  89. webClient.Credentials = CredentialCache.DefaultNetworkCredentials;
  90. int totalCount = 0;
  91. while (true)
  92. {
  93. var documents = GetString(webClient.DownloadData(exportSpec.InstanceUrl + "indexes?pageSize=128&start=" + totalCount));
  94. var array = RavenJArray.Parse(documents);
  95. if (array.Length == 0)
  96. {
  97. Console.WriteLine("Done with reading indexes, total: {0}", totalCount);
  98. break;
  99. }
  100. totalCount += array.Length;
  101. Console.WriteLine("Reading batch of {0,3} indexes, read so far: {1,10:#,#}", array.Length,
  102. totalCount);
  103. foreach (RavenJToken item in array)
  104. {
  105. item.WriteTo(jsonWriter);
  106. }
  107. }
  108. }
  109. jsonWriter.WriteEndArray();
  110. jsonWriter.WritePropertyName("Docs");
  111. jsonWriter.WriteStartArray();
  112. if (!exportSpec.ExportIndexesOnly)
  113. {
  114. ExportDocuments(exportSpec, jsonWriter);
  115. }
  116. jsonWriter.WriteEndArray();
  117. jsonWriter.WritePropertyName("Attachments");
  118. jsonWriter.WriteStartArray();
  119. if (exportSpec.IncludeAttachments)
  120. {
  121. ExportAttachments(jsonWriter, exportSpec);
  122. }
  123. jsonWriter.WriteEndArray();
  124. jsonWriter.WriteEndObject();
  125. streamWriter.Flush();
  126. }
  127. }
  128. private static void ExportDocuments(ExportSpec exportSpec, JsonTextWriter jsonWriter)
  129. {
  130. using (var webClient = new WebClient())
  131. {
  132. webClient.UseDefaultCredentials = true;
  133. webClient.Credentials = CredentialCache.DefaultNetworkCredentials;
  134. var lastEtag = Guid.Empty;
  135. int totalCount = 0;
  136. while (true)
  137. {
  138. var documents =
  139. GetString(webClient.DownloadData(exportSpec.InstanceUrl + "docs?pageSize=128&etag=" + lastEtag));
  140. var array = RavenJArray.Parse(documents);
  141. if (array.Length == 0)
  142. {
  143. Console.WriteLine("Done with reading documents, total: {0}", totalCount);
  144. break;
  145. }
  146. totalCount += array.Length;
  147. Console.WriteLine("Reading batch of {0,3} documents, read so far: {1,10:#,#}", array.Length,
  148. totalCount);
  149. foreach (RavenJToken item in array)
  150. {
  151. item.WriteTo(jsonWriter);
  152. }
  153. lastEtag = new Guid(array.Last().Value<RavenJObject>("@metadata").Value<string>("@etag"));
  154. }
  155. }
  156. }
  157. static void ExportAttachments(JsonTextWriter jsonWriter, ExportSpec exportSpec)
  158. {
  159. using (var webClient = new WebClient())
  160. {
  161. webClient.UseDefaultCredentials = true;
  162. webClient.Credentials = CredentialCache.DefaultNetworkCredentials;
  163. var lastEtag = Guid.Empty;
  164. int totalCount = 0;
  165. while (true)
  166. {
  167. var attachmentInfo = GetString(webClient.DownloadData(exportSpec.InstanceUrl + "/static/?pageSize=128&etag=" + lastEtag));
  168. var array = RavenJArray.Parse(attachmentInfo);
  169. if (array.Length == 0)
  170. {
  171. Console.WriteLine("Done with reading attachments, total: {0}", totalCount);
  172. break;
  173. }
  174. totalCount += array.Length;
  175. Console.WriteLine("Reading batch of {0,3} attachments, read so far: {1,10:#,#}", array.Length,
  176. totalCount);
  177. foreach (var item in array)
  178. {
  179. Console.WriteLine("Downloading attachment: {0}", item.Value<string>("Key"));
  180. var attachmentData = webClient.DownloadData(exportSpec.InstanceUrl + "/static/" + item.Value<string>("Key"));
  181. new RavenJObject
  182. {
  183. {"Data", attachmentData},
  184. {"Metadata", item.Value<RavenJObject>("Metadata")},
  185. {"Key", item.Value<string>("Key")}
  186. }
  187. .WriteTo(jsonWriter);
  188. }
  189. lastEtag = new Guid(array.Last().Value<string>("Etag"));
  190. }
  191. }
  192. }
  193. private class AttachmentExportInfo
  194. {
  195. public byte[] Data { get; set; }
  196. public RavenJObject Metadata { get; set; }
  197. public string Key { get; set; }
  198. }
  199. public static string GetString(byte[] downloadData)
  200. {
  201. var ms = new MemoryStream(downloadData);
  202. return new StreamReader(ms, Encoding.UTF8).ReadToEnd();
  203. }
  204. public static void ImportData(string instanceUrl, string file)
  205. {
  206. using (FileStream fileStream = File.OpenRead(file))
  207. {
  208. ImportData(fileStream, instanceUrl);
  209. }
  210. }
  211. public static void ImportData(Stream stream, string instanceUrl)
  212. {
  213. var sw = Stopwatch.StartNew();
  214. // Try to read the stream compressed, otherwise continue uncompressed.
  215. JsonTextReader jsonReader;
  216. try
  217. {
  218. var streamReader = new StreamReader(new GZipStream(stream, CompressionMode.Decompress));
  219. jsonReader = new JsonTextReader(streamReader);
  220. if (jsonReader.Read() == false)
  221. return;
  222. }
  223. catch (InvalidDataException)
  224. {
  225. stream.Seek(0, SeekOrigin.Begin);
  226. StreamReader streamReader = new StreamReader(stream);
  227. jsonReader = new JsonTextReader(streamReader);
  228. if (jsonReader.Read() == false)
  229. return;
  230. }
  231. if (jsonReader.TokenType != JsonToken.StartObject)
  232. throw new InvalidDataException("StartObject was expected");
  233. // should read indexes now
  234. if (jsonReader.Read() == false)
  235. return;
  236. if (jsonReader.TokenType != JsonToken.PropertyName)
  237. throw new InvalidDataException("PropertyName was expected");
  238. if (Equals("Indexes", jsonReader.Value) == false)
  239. throw new InvalidDataException("Indexes property was expected");
  240. if (jsonReader.Read() == false)
  241. return;
  242. if (jsonReader.TokenType != JsonToken.StartArray)
  243. throw new InvalidDataException("StartArray was expected");
  244. using (var webClient = new WebClient())
  245. {
  246. webClient.UseDefaultCredentials = true;
  247. webClient.Headers.Add("Content-Type", "application/json; charset=utf-8");
  248. webClient.Credentials = CredentialCache.DefaultNetworkCredentials;
  249. while (jsonReader.Read() && jsonReader.TokenType != JsonToken.EndArray)
  250. {
  251. var index = RavenJToken.ReadFrom(jsonReader);
  252. var indexName = index.Value<string>("name");
  253. if (indexName.StartsWith("Raven/") || indexName.StartsWith("Temp/"))
  254. continue;
  255. using (var streamWriter = new StreamWriter(webClient.OpenWrite(instanceUrl + "indexes/" + indexName, "PUT")))
  256. using (var jsonTextWriter = new JsonTextWriter(streamWriter))
  257. {
  258. index.Value<RavenJObject>("definition").WriteTo(jsonTextWriter);
  259. jsonTextWriter.Flush();
  260. streamWriter.Flush();
  261. }
  262. }
  263. }
  264. // should read documents now
  265. if (jsonReader.Read() == false)
  266. return;
  267. if (jsonReader.TokenType != JsonToken.PropertyName)
  268. throw new InvalidDataException("PropertyName was expected");
  269. if (Equals("Docs", jsonReader.Value) == false)
  270. throw new InvalidDataException("Docs property was expected");
  271. if (jsonReader.Read() == false)
  272. return;
  273. if (jsonReader.TokenType != JsonToken.StartArray)
  274. throw new InvalidDataException("StartArray was expected");
  275. var batch = new List<RavenJObject>();
  276. int totalCount = 0;
  277. while (jsonReader.Read() && jsonReader.TokenType != JsonToken.EndArray)
  278. {
  279. totalCount += 1;
  280. var document = RavenJToken.ReadFrom(jsonReader);
  281. batch.Add((RavenJObject)document);
  282. if (batch.Count >= 128)
  283. FlushBatch(instanceUrl, batch);
  284. }
  285. FlushBatch(instanceUrl, batch);
  286. var attachmentCount = 0;
  287. if (jsonReader.Read() == false || jsonReader.TokenType == JsonToken.EndObject)
  288. return;
  289. if (jsonReader.TokenType != JsonToken.PropertyName)
  290. throw new InvalidDataException("PropertyName was expected");
  291. if (Equals("Attachments", jsonReader.Value) == false)
  292. throw new InvalidDataException("Attachment property was expected");
  293. if (jsonReader.Read() == false)
  294. return;
  295. if (jsonReader.TokenType != JsonToken.StartArray)
  296. throw new InvalidDataException("StartArray was expected");
  297. while (jsonReader.Read() && jsonReader.TokenType != JsonToken.EndArray)
  298. {
  299. using (var client = new WebClient())
  300. {
  301. attachmentCount += 1;
  302. var item = RavenJToken.ReadFrom(jsonReader);
  303. var attachmentExportInfo =
  304. new JsonSerializer
  305. {
  306. Converters = {new TrivialJsonToJsonJsonConverter()}
  307. }.Deserialize<AttachmentExportInfo>(new RavenJTokenReader(item));
  308. Console.WriteLine("Importing attachment {0}", attachmentExportInfo.Key);
  309. if (attachmentExportInfo.Metadata != null)
  310. {
  311. foreach (var header in attachmentExportInfo.Metadata)
  312. {
  313. client.Headers.Add(header.Key, StripQuotesIfNeeded(header.Value));
  314. }
  315. }
  316. using (var writer = client.OpenWrite(instanceUrl + "static/" + attachmentExportInfo.Key, "PUT"))
  317. {
  318. writer.Write(attachmentExportInfo.Data, 0, attachmentExportInfo.Data.Length);
  319. writer.Flush();
  320. }
  321. }
  322. }
  323. Console.WriteLine("Imported {0:#,#} documents and {1:#,#} attachments in {2:#,#} ms", totalCount, attachmentCount, sw.ElapsedMilliseconds);
  324. }
  325. public class TrivialJsonToJsonJsonConverter : JsonConverter
  326. {
  327. public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
  328. {
  329. throw new NotImplementedException();
  330. }
  331. public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
  332. {
  333. return RavenJObject.Load(reader);
  334. }
  335. public override bool CanConvert(Type objectType)
  336. {
  337. return objectType == typeof (RavenJObject);
  338. }
  339. }
  340. private static string StripQuotesIfNeeded(RavenJToken value)
  341. {
  342. var str = value.ToString(Formatting.None);
  343. if (str.StartsWith("\"") && str.EndsWith("\""))
  344. return str.Substring(1, str.Length - 2);
  345. return str;
  346. }
  347. private static void FlushBatch(string instanceUrl, List<RavenJObject> batch)
  348. {
  349. var sw = Stopwatch.StartNew();
  350. long size;
  351. using (var webClient = new WebClient())
  352. {
  353. webClient.Headers.Add("Content-Type", "application/json; charset=utf-8");
  354. webClient.UseDefaultCredentials = true;
  355. webClient.Credentials = CredentialCache.DefaultNetworkCredentials;
  356. using (var stream = new MemoryStream())
  357. {
  358. using (var streamWriter = new StreamWriter(stream, Encoding.UTF8))
  359. using (var jsonTextWriter = new JsonTextWriter(streamWriter))
  360. {
  361. var commands = new RavenJArray();
  362. foreach (var doc in batch)
  363. {
  364. var metadata = doc.Value<RavenJObject>("@metadata");
  365. doc.Remove("@metadata");
  366. commands.Add(new RavenJObject
  367. {
  368. {"Method", "PUT"},
  369. {"Document", doc},
  370. {"Metadata", metadata},
  371. {"Key", metadata.Value<string>("@id")}
  372. });
  373. }
  374. commands.WriteTo(jsonTextWriter);
  375. jsonTextWriter.Flush();
  376. streamWriter.Flush();
  377. stream.Flush();
  378. size = stream.Length;
  379. using (var netStream = webClient.OpenWrite(instanceUrl + "bulk_docs", "POST"))
  380. {
  381. stream.WriteTo(netStream);
  382. netStream.Flush();
  383. }
  384. }
  385. }
  386. }
  387. Console.WriteLine("Wrote {0} documents [{1:#,#} kb] in {2:#,#} ms",
  388. batch.Count, Math.Round((double)size / 1024, 2), sw.ElapsedMilliseconds);
  389. batch.Clear();
  390. }
  391. }
  392. }