Esempio n. 1
0
        /// <summary>
        /// Initializes a new instance of the <see cref="JsonStoreWriter"/> class.
        /// </summary>
        /// <param name="name">The name of the application that generated the persisted files, or the root name of the files.</param>
        /// <param name="path">The directory in which the main persisted file resides or will reside, or null to create a volatile data store.</param>
        /// <param name="createSubdirectory">If true, a numbered subdirectory is created for this store.</param>
        /// <param name="extension">The extension for the underlying file.</param>
        public JsonStoreWriter(string name, string path, bool createSubdirectory = true, string extension = DefaultExtension)
            : base(extension)
        {
            ushort id = 0;

            this.Name = name;
            this.Path = System.IO.Path.GetFullPath(path);
            if (createSubdirectory)
            {
                // if the root directory already exists, look for the next available id
                if (Directory.Exists(this.Path))
                {
                    var existingIds = Directory.EnumerateDirectories(this.Path, this.Name + ".????")
                                      .Select(d => d.Split('.').Last())
                                      .Where(n => ushort.TryParse(n, out ushort i))
                                      .Select(n => ushort.Parse(n));
                    id = (ushort)(existingIds.Count() == 0 ? 0 : existingIds.Max() + 1);
                }

                this.Path = System.IO.Path.Combine(this.Path, $"{this.Name}.{id:0000}");
            }

            if (!Directory.Exists(this.Path))
            {
                Directory.CreateDirectory(this.Path);
            }

            string dataPath = System.IO.Path.Combine(this.Path, StoreCommon.GetDataFileName(this.Name) + this.Extension);

            this.streamWriter = File.CreateText(dataPath);
            this.jsonWriter   = new JsonTextWriter(this.streamWriter);
            this.jsonWriter.WriteStartArray();
        }
Esempio n. 2
0
        /// <summary>
        /// Initializes a new instance of the <see cref="JsonStoreReader"/> class.
        /// </summary>
        /// <param name="name">The name of the application that generated the persisted files, or the root name of the files</param>
        /// <param name="path">The directory in which the main persisted file resides or will reside, or null to create a volatile data store</param>
        /// <param name="dataSchemaString">JSON schema used to validate data stream.</param>
        /// <param name="extension">The extension for the underlying file.</param>
        /// <param name="preloadSchemas">Dictionary of URis to JSON schemas to preload before validating any JSON. Would likely include schemas references by the catalog and data schemas.</param>
        public JsonStoreReader(string name, string path, string dataSchemaString, string extension = DefaultExtension, IDictionary <Uri, string> preloadSchemas = null)
            : base(dataSchemaString, extension, preloadSchemas)
        {
            this.Name = name;
            this.Path = StoreCommon.GetPathToLatestVersion(name, path);

            // load catalog
            string metadataPath = System.IO.Path.Combine(this.Path, StoreCommon.GetCatalogFileName(this.Name) + this.Extension);

            using (var file = File.OpenText(metadataPath))
                using (var reader = new JsonTextReader(file))
                    using (var validatingReader = new JSchemaValidatingReader(reader))
                    {
                        validatingReader.Schema = this.CatalogSchema;
                        validatingReader.ValidationEventHandler += (s, e) => throw new InvalidDataException(e.Message);
                        this.catalog = this.Serializer.Deserialize <List <JsonStreamMetadata> >(validatingReader);
                    }

            // compute originating time interval
            this.originatingTimeInterval = TimeInterval.Empty;
            foreach (var metadata in this.catalog)
            {
                var metadataTimeInterval = new TimeInterval(metadata.FirstMessageOriginatingTime, metadata.LastMessageOriginatingTime);
                this.originatingTimeInterval = TimeInterval.Coverage(new TimeInterval[] { this.originatingTimeInterval, metadataTimeInterval });
            }
        }
Esempio n. 3
0
        private void WriteCatalog()
        {
            string metadataPath = System.IO.Path.Combine(this.Path, StoreCommon.GetCatalogFileName(this.Name) + this.Extension);

            using (var file = File.CreateText(metadataPath))
                using (var writer = new JsonTextWriter(file))
                {
                    this.Serializer.Serialize(writer, this.catalog.Values.ToList());
                }
        }
Esempio n. 4
0
        private void WriteCatalog()
        {
            string metadataPath = System.IO.Path.Combine(this.Path, StoreCommon.GetCatalogFileName(this.Name) + this.Extension);

            using (var file = File.CreateText(metadataPath))
                using (var writer = new JsonTextWriter(file))
                    using (var validatingWriter = new JSchemaValidatingWriter(writer))
                    {
                        validatingWriter.Schema = this.CatalogSchema;
                        validatingWriter.ValidationEventHandler += (s, e) => throw new InvalidDataException(e.Message);
                        this.Serializer.Serialize(validatingWriter, this.catalog.Values.ToList());
                    }
        }
Esempio n. 5
0
        /// <summary>
        /// Seek to envelope in stream according to specified replay descriptor.
        /// </summary>
        /// <param name="descriptor">The replay descriptor.</param>
        public void Seek(ReplayDescriptor descriptor)
        {
            this.descriptor = descriptor;

            // load data
            string dataPath = System.IO.Path.Combine(this.Path, StoreCommon.GetDataFileName(this.Name) + this.Extension);

            this.streamReader?.Dispose();
            this.streamReader     = File.OpenText(dataPath);
            this.jsonReader       = new JsonTextReader(this.streamReader);
            this.validatingReader = new JSchemaValidatingReader(this.jsonReader)
            {
                Schema = this.DataSchema
            };
            this.validatingReader.ValidationEventHandler += (s, e) => throw new InvalidDataException(e.Message);

            // iterate through data store until we either reach the end or we find the start of the replay descriptor
            while (this.validatingReader.Read())
            {
                // data stores are arrays of messages, messages start as objects
                if (this.validatingReader.TokenType == JsonToken.StartObject)
                {
                    // read envelope
                    if (!this.validatingReader.Read() || !this.ReadEnvelope(out this.envelope))
                    {
                        throw new InvalidDataException("Messages must be an ordered object: {\"Envelope\": <Envelope>, \"Data\": <Data>}. Deserialization needs to read the envelope before the data to know what type of data to deserialize.");
                    }

                    if (this.descriptor.Interval.Left < (descriptor.UseOriginatingTime ? this.envelope.OriginatingTime : this.envelope.Time))
                    {
                        // found start of interval
                        break;
                    }

                    // skip data
                    if (!this.ReadData(out this.data))
                    {
                        throw new InvalidDataException("Messages must be an ordered object: {\"Envelope\": <Envelope>, \"Data\": <Data>}. Deserialization needs to read the envelope before the data to know what type of data to deserialize.");
                    }
                }
            }
        }
Esempio n. 6
0
        /// <summary>
        /// Repairs an invalid store.
        /// </summary>
        /// <param name="name">The name of the store to check.</param>
        /// <param name="path">The path of the store to check.</param>
        /// <param name="deleteOldStore">Indicates whether the original store should be deleted.</param>
        public static void Repair(string name, string path, bool deleteOldStore = true)
        {
            string storePath      = StoreCommon.GetPathToLatestVersion(name, path);
            string tempFolderPath = Path.Combine(path, $"Repair-{Guid.NewGuid()}");

            // call Crop over the entire store interval to regenerate and repair the streams in the store
            Store.Crop((name, storePath), (name, tempFolderPath), TimeInterval.Infinite);

            // create a _BeforeRepair folder in which to save the original store files
            var beforeRepairPath = Path.Combine(storePath, $"BeforeRepair-{Guid.NewGuid()}");

            Directory.CreateDirectory(beforeRepairPath);

            // Move the original store files to the BeforeRepair folder. Do this even if the deleteOldStore
            // flag is true, as deleting the original store files immediately may occasionally fail. This can
            // happen because the InfiniteFileReader disposes of its MemoryMappedView in a background
            // thread, which may still be in progress. If deleteOldStore is true, we will delete the
            // BeforeRepair folder at the very end (by which time any open MemoryMappedViews will likely
            // have finished disposing).
            foreach (var file in Directory.EnumerateFiles(storePath))
            {
                var fileInfo = new FileInfo(file);
                File.Move(file, Path.Combine(beforeRepairPath, fileInfo.Name));
            }

            // move the repaired store files to the original folder
            foreach (var file in Directory.EnumerateFiles(Path.Combine(tempFolderPath, $"{name}.0000")))
            {
                var fileInfo = new FileInfo(file);
                File.Move(file, Path.Combine(storePath, fileInfo.Name));
            }

            // cleanup temporary folder
            Directory.Delete(tempFolderPath, true);

            if (deleteOldStore)
            {
                // delete the old store files
                Directory.Delete(beforeRepairPath, true);
            }
        }
Esempio n. 7
0
        /// <summary>
        /// Gets the simple reader for the specified stream binding.
        /// </summary>
        /// <param name="streamBinding">The stream binding.</param>
        /// <returns>The simple reader.</returns>
        public ISimpleReader GetReader(StreamBinding streamBinding)
        {
            if (streamBinding == null)
            {
                throw new ArgumentNullException(nameof(streamBinding));
            }

            var key = Tuple.Create(streamBinding.StoreName, streamBinding.StorePath);

            if (this.dataStoreReaders.Contains(key))
            {
                return(this.dataStoreReaders[key].GetReader());
            }

            key = Tuple.Create(streamBinding.StoreName, StoreCommon.GetPathToLatestVersion(streamBinding.StoreName, streamBinding.StorePath));
            if (this.dataStoreReaders.Contains(key))
            {
                return(this.dataStoreReaders[key].GetReader());
            }

            return(null);
        }
Esempio n. 8
0
        /// <summary>
        /// Initializes a new instance of the <see cref="JsonStoreReader"/> class.
        /// </summary>
        /// <param name="name">The name of the application that generated the persisted files, or the root name of the files.</param>
        /// <param name="path">The directory in which the main persisted file resides or will reside, or null to create a volatile data store.</param>
        /// <param name="extension">The extension for the underlying file.</param>
        public JsonStoreReader(string name, string path, string extension = DefaultExtension)
            : base(extension)
        {
            this.Name = name;
            this.Path = StoreCommon.GetPathToLatestVersion(name, path);

            // load catalog
            string metadataPath = System.IO.Path.Combine(this.Path, StoreCommon.GetCatalogFileName(this.Name) + this.Extension);

            using (var file = File.OpenText(metadataPath))
                using (var reader = new JsonTextReader(file))
                {
                    this.catalog = this.Serializer.Deserialize <List <JsonStreamMetadata> >(reader);
                }

            // compute originating time interval
            this.originatingTimeInterval = TimeInterval.Empty;
            foreach (var metadata in this.catalog)
            {
                var metadataTimeInterval = new TimeInterval(metadata.FirstMessageOriginatingTime, metadata.LastMessageOriginatingTime);
                this.originatingTimeInterval = TimeInterval.Coverage(new TimeInterval[] { this.originatingTimeInterval, metadataTimeInterval });
            }
        }
Esempio n. 9
0
        /// <summary>
        /// Initializes a new instance of the <see cref="JsonStoreWriter"/> class.
        /// </summary>
        /// <param name="name">The name of the application that generated the persisted files, or the root name of the files</param>
        /// <param name="path">The directory in which the main persisted file resides or will reside, or null to create a volatile data store</param>
        /// <param name="dataSchemaString">JSON schema used to validate data stream.</param>
        /// <param name="createSubdirectory">If true, a numbered subdirectory is created for this store</param>
        /// <param name="extension">The extension for the underlying file.</param>
        /// <param name="preloadSchemas">Dictionary of URis to JSON schemas to preload before validating any JSON. Would likely include schemas references by the catalog and data schemas.</param>
        public JsonStoreWriter(string name, string path, string dataSchemaString, bool createSubdirectory = true, string extension = DefaultExtension, IDictionary <Uri, string> preloadSchemas = null)
            : base(dataSchemaString, extension, preloadSchemas)
        {
            ushort id = 0;

            this.Name = name;
            this.Path = System.IO.Path.GetFullPath(path);
            if (createSubdirectory)
            {
                // if the root directory already exists, look for the next available id
                if (Directory.Exists(this.Path))
                {
                    var existingIds = Directory.EnumerateDirectories(this.Path, this.Name + ".????")
                                      .Select(d => d.Split('.').Last())
                                      .Where(n => ushort.TryParse(n, out ushort i))
                                      .Select(n => ushort.Parse(n));
                    id = (ushort)(existingIds.Count() == 0 ? 0 : existingIds.Max() + 1);
                }

                this.Path = System.IO.Path.Combine(this.Path, $"{this.Name}.{id:0000}");
            }

            if (!Directory.Exists(this.Path))
            {
                Directory.CreateDirectory(this.Path);
            }

            string dataPath = System.IO.Path.Combine(this.Path, StoreCommon.GetDataFileName(this.Name) + this.Extension);

            this.streamWriter            = File.CreateText(dataPath);
            this.jsonWriter              = new JsonTextWriter(this.streamWriter);
            this.validatingWriter        = new JSchemaValidatingWriter(this.jsonWriter);
            this.validatingWriter.Schema = this.DataSchema;
            this.validatingWriter.ValidationEventHandler += (s, e) => throw new InvalidDataException(e.Message);
            this.validatingWriter.WriteStartArray();
        }
Esempio n. 10
0
 /// <summary>
 /// Indicates whether the specified store file exists.
 /// </summary>
 /// <param name="name">The name of the store to check.</param>
 /// <param name="path">The path of the store to check.</param>
 /// <returns>Returns true if the store exists.</returns>
 public static bool Exists(string name, string path)
 {
     return((path == null) ? InfiniteFileReader.IsActive(StoreCommon.GetCatalogFileName(name), path) : StoreCommon.TryGetPathToLatestVersion(name, path, out _));
 }