private Wireup(ConventionWireupParameters conventionWireup, IDictionary <string, Type> aliasTypes, IEnumerable <Type> transientTypes, IEnumerable <Assembly> assemblies) { Log.Info("Preparing to bootstrap the system."); var repository = new DefaultRepository(new ConventionRoutingTable(assemblies)); var persistenceFactory = new PersistenceFactory(conventionWireup.JournalConnectionName, conventionWireup.DuplicateWindow, conventionWireup.JournalBatchSize); var snapshotFactory = new SnapshotFactory(conventionWireup.SnapshotLocation, conventionWireup.PublicSnapshotConnectionName); var persistenceBootstrapper = new PersistenceBootstrapper(persistenceFactory); Log.Info("Connecting to message store."); this.info = persistenceBootstrapper.Restore(); var duplicates = new DuplicateStore(conventionWireup.DuplicateWindow, this.info.DuplicateIdentifiers); var timeoutFactory = new TimeoutFactory(); var messagingFactory = new MessagingFactory(conventionWireup.NodeId, conventionWireup.BrokerAddress, conventionWireup.SourceQueueName, duplicates); Log.Info("Loading bootstrap parameters."); var messageStore = persistenceFactory.CreateMessageStore(this.info.SerializedTypes); var disruptorFactory = new DisruptorFactory(messagingFactory, persistenceFactory, snapshotFactory, conventionWireup.SystemSnapshotFrequency, aliasTypes, transientTypes); var snapshotBootstrapper = new SnapshotBootstrapper(snapshotFactory, disruptorFactory, conventionWireup.SystemSnapshotFrequency); var messageBootstrapper = new MessageBootstrapper(messageStore, disruptorFactory); this.bootstrapper = new Bootstrapper(repository, disruptorFactory, snapshotBootstrapper, messageBootstrapper, timeoutFactory, messagingFactory); }
/// <summary> /// Runs the rules /// </summary> /// <param name="sender"></param> /// <param name="e">args</param> private void Run(object sender, DoWorkEventArgs e) { try { if (!(e.Argument is RulesRunnerDoWorkArgs args)) { e.Result = null; throw new ArgumentException("RulesRunner expects arguments of type RulesRunnerDoWorkArgs."); } var stopwatch = DateTime.Now; // clear out our duplicate tracking each run DuplicateStore.Reset(); // create the connection manager and bubble up any messages var connections = new ConnectionManager(); connections.OnStatusUpdate += (string message) => { this.OnStatusUpdate?.Invoke(message); }; // used to collect the results as we process rule collections var results = new RulesRunnerResult(); connections.WithConnections((LdapConnection connection, string distinguishedName) => { if (this.CancellationPending) { e.Result = null; return; } // we try and create a key from the queried directory and fail to random guid var servers = ((LdapDirectoryIdentifier)connection.Directory).Servers; var identifier = servers.Length > 0 ? servers.First() : Guid.NewGuid().ToString("D"); // get the rule collection to run var ruleCollection = this.GetRuleCollection(distinguishedName); // runs the rule collection var result = this.ExecuteRuleCollection(ruleCollection, connection); // handle the cancel case if (result == null && this.CancellationPending) { e.Result = null; return; } // add our result to the list results.Add(identifier, result); }); // we pass back the collection of all results for processing into the UI grid e.Result = results; } catch (Exception err) { this.OnStatusUpdate?.Invoke("Error in RulesRunner: " + err.Message); e.Result = err; } finally { DuplicateStore.Reset(); } }
/// <summary> /// Executes implementation for this rule /// </summary> /// <param name="parent">The composed rule containing this rule</param> /// <param name="entry">The search result we are checking</param> /// <param name="attributeValue">The current attribute value as pass through the chain</param> /// <returns>Either a success or error result</returns> public override RuleResult Execute(ComposedRule parent, SearchResultEntry entry, string attributeValue) { // record in memory the entry details with a list of the attributes that end up needing to be checked if (DuplicateStore.IsDuplicate(parent.AttributeName, attributeValue, entry)) { // we now need to execute some complicated logic from the original application depending // settings, etc. this isn't pretty but it follows the original for now var actionType = ActionType.Edit; var originalValue = entry.Attributes[parent.AttributeName][0].ToString(); // if nothing else has changed the value prior to this (which is why no duplicate rule needs to be run last) if (originalValue == attributeValue) { switch (parent.AttributeName.ToLowerInvariant()) { case "proxyaddresses": if (Constants.SMTPRegex.IsMatch(attributeValue)) { if (entry.Attributes.Contains(StringLiterals.MailNickName)) { if (attributeValue.ToLowerInvariant().Substring(0, 5) == "smtp:") { actionType = ActionType.Complete; } } else { actionType = ActionType.Remove; } } break; case "userprincipalname": if (entry.Attributes.Contains(StringLiterals.MailNickName)) { actionType = ActionType.Complete; } break; case "mail": if (entry.Attributes.Contains(StringLiterals.MailNickName)) { actionType = ActionType.Complete; } break; case "mailnickname": if (entry.Attributes.Contains(StringLiterals.HomeMdb)) { actionType = ActionType.Complete; } break; case "samaccountname": if (entry.Attributes.Contains(StringLiterals.MailNickName)) { actionType = ActionType.Complete; } break; } } return(this.GetErrorResult(ErrorType.Duplicate, attributeValue, actionType)); } return(this.GetSuccessResult()); }
/// <summary> /// Executes a rule collection against the supplied connection /// </summary> /// <param name="collection">The rule collection to execute</param> /// <param name="connection">The connection used to retrieve entries for processing</param> /// <returns><see cref="RuleCollectionResult"/> or null if canceled</returns> private RuleCollectionResult ExecuteRuleCollection(RuleCollection collection, LdapConnection connection) { // these count all the totals for the connection against which this RuleCollection is being run var stopWatch = new Stopwatch(); long skipCount = 0; long entryCount = 0; long duplicateCount = 0; long errorCount = 0; this.OnStatusUpdate?.Invoke(StringLiterals.LdapConnectionEstablishing); var searchRequest = collection.CreateSearchRequest(); this.OnStatusUpdate?.Invoke(StringLiterals.LdapConnectionEstablished); var errors = new List <ComposedRuleResult>(); this.OnStatusUpdate?.Invoke(StringLiterals.BeginningQuery); while (true) { var searchResponse = (SearchResponse)connection.SendRequest(searchRequest); // verify support for paged results if (searchResponse.Controls.Length != 1 || !(searchResponse.Controls[0] is PageResultResponseControl)) { this.OnStatusUpdate?.Invoke(StringLiterals.CannotPageResultSet); throw new InvalidOperationException(StringLiterals.CannotPageResultSet); } foreach (SearchResultEntry entry in searchResponse.Entries) { if (this.CancellationPending) { return(null); } if (collection.Skip(entry)) { skipCount++; continue; } // this tracks the number of entries we have processed and not skipped entryCount++; foreach (var composedRule in collection.Rules) { // run each composed rule which can produce multiple results var results = composedRule.Execute(entry); for (var i = 0; i < results.Length; i++) { var result = results[i]; if (!result.Success) { errorCount++; if (result.Results.Any(r => (r.ErrorTypeFlags & ErrorType.Duplicate) != 0)) { duplicateCount++; if (result.ProposedAction == ActionType.Edit) { // Add original LDAP entry with the same value. var originalEntry = DuplicateStore.GetOriginalSearchResultEntry(result.AttributeName, result.OriginalValue); var additionalResult = new ComposedRuleResult { AttributeName = result.AttributeName, EntityDistinguishedName = originalEntry.DistinguishedName, EntityCommonName = originalEntry.Attributes[StringLiterals.Cn][0].ToString(), ObjectType = ComposedRule.GetObjectType(entry), OriginalValue = result.OriginalValue, ProposedAction = result.ProposedAction, ProposedValue = result.ProposedValue, Results = new RuleResult[] { new RuleResult(false) { ErrorTypeFlags = ErrorType.Duplicate, ProposedAction = result.ProposedAction, UpdatedValue = result.OriginalValue } }, Success = result.Success }; errors.Add(additionalResult); } } errors.Add(result); } } } } // handle paging var cookie = searchResponse.Controls.OfType <PageResultResponseControl>().First().Cookie; // if this is true, there are no more pages to request if (cookie.Length == 0) { break; } searchRequest.Controls.OfType <PageResultRequestControl>().First().Cookie = cookie; } // we are all done, stop tracking time stopWatch.Stop(); return(new RuleCollectionResult { TotalDuplicates = duplicateCount, TotalErrors = errorCount, TotalFound = skipCount + entryCount, TotalSkips = skipCount, TotalProcessed = entryCount, Elapsed = stopWatch.Elapsed, Errors = errors.ToArray() }); }
/// <summary> /// Runs the rules /// </summary> /// <param name="sender"></param> /// <param name="e">args</param> private void Run(object sender, DoWorkEventArgs e) { try { if (!(e.Argument is RulesRunnerDoWorkArgs args)) { e.Result = null; throw new ArgumentException("RulesRunner expects arguments of type RulesRunnerDoWorkArgs."); } var stopwatch = DateTime.Now; // clear out our duplicate tracking each run DuplicateStore.Reset(); // create the connection manager and bubble up any messages var connections = new ConnectionManager(); connections.OnStatusUpdate += (string message) => { this.OnStatusUpdate?.Invoke(message); }; // used to collect the results as we process rule collections var results = new RulesRunnerResult(); connections.WithConnections((LdapConnection connection, string distinguishedName) => { if (this.CancellationPending) { e.Result = null; return; } // we try and create a key from the queried directory and fail to random guid var servers = ((LdapDirectoryIdentifier)connection.Directory).Servers; var identifier = servers.Length > 0 ? servers.First() : Guid.NewGuid().ToString("D"); // get the rule collection to run var ruleCollection = this.GetRuleCollection(distinguishedName); // if connecting to Global Catalog Server, make sure the collection's analyzed attributes are replicated. if (SettingsManager.Instance.Port == Constants.GlobalCatalogPort) { var schemaResult = this.ExecuteRuleCollectionSchemaCheck(ruleCollection, connection); // handle the cancel case if (schemaResult == null && this.CancellationPending) { e.Result = null; return; } // display errors if (schemaResult.Count > 0) { DialogResult dialogResult = DialogResult.None; // Show message box on main thread. FormApp.Instance.Invoke(new Action(() => { var message = string.Format(StringLiterals.SchemaWarningMessage, Environment.NewLine + string.Join(Environment.NewLine, schemaResult.ToArray())); dialogResult = MessageBox.Show(FormApp.Instance, message, StringLiterals.SchemaWarningTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Warning); })); if (dialogResult == DialogResult.No) { return; } } } // runs the rule collection var result = this.ExecuteRuleCollection(ruleCollection, connection); // handle the cancel case if (result == null && this.CancellationPending) { e.Result = null; return; } // add our result to the list results.Add(identifier, result); }); // we pass back the collection of all results for processing into the UI grid e.Result = results; } catch (Exception err) { this.OnStatusUpdate?.Invoke("Error in RulesRunner: " + err.Message); e.Result = err; } finally { DuplicateStore.Reset(); } }