/// <summary> /// Initializes a new instance of <see cref="VirtualizedProcess"/>, /// using the <see cref="ProcessSynchronizer"/> specified. /// </summary> /// <param name="startInfo"> /// The <see cref="VirtualProcessStartInfo"/> containing the information used to start the process with. /// </param> /// <param name="processSynchronizer"> /// The <see cref="IProcessSynchronizer"/> to use for data synchronization with the <see cref="VirtualizedProcess"/>. /// </param> protected VirtualizedProcess(VirtualProcessStartInfo startInfo, IProcessSynchronizer processSynchronizer) { _exitEventSyncRoot = new object(); _startInfo = startInfo; _connection = new ConnectionManager(processSynchronizer); _gacManager = new GacManager(startInfo.Files.Executable.FileName, HostCore.Configuration.Application.LibsToShare); }
/// <summary> /// Initializes a new instance of <see cref="ProcessEntryPoint"/>. /// </summary> /// <remarks> /// This call should only focus on initializing the variables and connecting to the host application. /// All business logic should be invoked in the <see cref="Run"/> method. /// Unhandled exception are redirected to the host application automatically. /// </remarks> /// <param name="inContext">Information about the environment in which the library main method has been invoked.</param> /// <param name="inChannelName">The name of the inter-process communication channel to connect to.</param> public ProcessEntryPoint(RemoteHooking.IContext inContext, string inChannelName) { // Name the current thread. if (Thread.CurrentThread.Name == null) { Thread.CurrentThread.Name = "Initializer"; } // Connect to server. IProcessSynchronizer sync = RemoteHooking.IpcConnectClient <ProcessSynchronizerInterface>(inChannelName).ProcessSynchronizer; // Initialize the guest's core. EngineCore.Initialize(sync); // Validate connection. if (!EngineCore.Connected) { throw new EngineException("Failed to validate the inter-process connection while initializing the guest's virtual environment."); } }
/// <summary> /// Initializes the <see cref="EngineCore"/>. /// </summary> /// <param name="processSynchronizer"> /// The <see cref="IProcessSynchronizer"/> to use for communication with the host. /// </param> public static void Initialize(IProcessSynchronizer processSynchronizer) { lock (_syncRoot) { if (_initialized) { return; } // Initialize variables. _currentProcessId = RemoteHooking.GetCurrentProcessId(); _serverReporter = processSynchronizer; _logBus = new LogBus(processSynchronizer); #if !SYNCLOG _logBus.Enabled = true; #endif _engine = VirtualizationEngine.InitializeEngine(processSynchronizer); // Attach ProcessExit event handler. AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit; // Mark instance as initialized. _initialized = true; Log.Message("Successfully initialized core components."); } }
/// <summary> /// Gets the last successful session. /// </summary> /// <param name="synchronizer">The synchronizer.</param> /// <returns>IESyncSessionInfo.</returns> private IESyncSessionInfo GetLastSuccessfulSession(IProcessSynchronizer synchronizer) { try { var filterList = new FilterList { new FilterDescriptor("SynchronizedProcessName", FilterOperator.IsEqualTo, synchronizer.ProcessName), new FilterDescriptor(Constants.ESyncSessionSyncProcessGuidColumnName, FilterOperator.IsEqualTo, synchronizer.Guid.ToString()), new FilterDescriptor(Constants.ESyncSessionIsSuccessfulColumnName, FilterOperator.IsEqualTo, 1), }; var sortList = new SortList { new SortDescriptor(Constants.ESyncSessionEndTimeColumnName, SortDirection.Descending) }; var list = DynamicTypeManager.GetList<IInfoList>(Constants.ESyncSessionProcessName, string.Empty, 0, 1, sortList, filterList); if (list != null && list.Count > 0) return list.Cast<IESyncSessionInfo>().First(); } catch (Exception ex) { Logger.Log(LogSeverity.Error, "ESYNC", ex); } return null; }
/// <summary> /// Updates the items. /// </summary> /// <param name="job">The job.</param> /// <param name="itemsToSynchronize">The items to synchronize.</param> /// <param name="fieldUpdaters">The field updaters.</param> /// <param name="dynamicItem">The dynamic item.</param> /// <param name="context">The context.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> private bool UpdateItems( IProcessSynchronizer job, IEnumerable<int> itemsToSynchronize, IEnumerable<IFieldUpdater> fieldUpdaters, ISyncDynamicItem dynamicItem, SynchronizationContext context) { return itemsToSynchronize.Aggregate(true, (current, id) => current && ExecuteUpdate(job, id, fieldUpdaters, dynamicItem, context)); }
/// <summary> /// Executes the update. /// </summary> /// <param name="job">The job.</param> /// <param name="itemId">The item identifier.</param> /// <param name="fieldUpdaters">The field updaters.</param> /// <param name="dynamicItem">The dynamic item.</param> /// <param name="context">The context.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> private bool ExecuteUpdate( IProcessSynchronizer job, int itemId, IEnumerable<IFieldUpdater> fieldUpdaters, ISyncDynamicItem dynamicItem, SynchronizationContext context) { try { var criteria = new DetailCriteria(itemId) { AllowLazyLoading = true }; var editItem = DynamicTypeManager.GetEditableRoot<IEditableRoot>(job.ProcessName, criteria); if (editItem == null || editItem.Id != itemId) { context.AddError(string.Format(CultureInfo.InvariantCulture, "Failed to load item with Id = {0}.", itemId)); return false; } foreach (var fieldUpdater in fieldUpdaters) { fieldUpdater.Update(dynamicItem, editItem); if (fieldUpdater.IsKey) { editItem.KeyFields.Add(fieldUpdater.FieldName); } } if (!((ITrackStatus)editItem).IsValid) { AddValidationErrors(editItem, context); return false; } ((ISavable)editItem).Save(); return true; } catch (Exception ex) { context.AddException(ex); } return false; }
/// <summary> /// Synchronizes the specified job. /// </summary> /// <param name="job">The job.</param> /// <param name="filterBuilders">The filter builders.</param> /// <param name="fieldUpdaters">The field updaters.</param> /// <param name="item">The item.</param> /// <param name="context">The context.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> /// <exception cref="System.ArgumentException">At least one key field should be specified.</exception> /// <exception cref="System.NotSupportedException"></exception> private bool Synchronize( IProcessSynchronizer job, IList<IFieldFilterBuilder> filterBuilders, IList<IFieldUpdater> fieldUpdaters, ProviderItem item, SynchronizationContext context) { if (filterBuilders.Count == 0) throw new ArgumentException("At least one key field should be specified."); var failedAttempts = 0; Random random = null; while (true) { try { using (var transactionScope = TransactionUtils.CreateTransactionScope()) { var dynamicItem = job.CreateDynamicItem(item); var filterList = filterBuilders.Select(filterBuilder => filterBuilder.GetFilter(dynamicItem)).ToList(); var itemsToSynchronize = RuntimeDatabase.FindItems(job.ProcessName, filterList); bool isSuccessful; switch (job.Action) { case ESyncAction.Insert: isSuccessful = itemsToSynchronize.Count > 0 || ExecuteInsert(job, fieldUpdaters, dynamicItem, context); break; case ESyncAction.Update: isSuccessful = UpdateItems(job, itemsToSynchronize, fieldUpdaters, dynamicItem, context); break; case ESyncAction.InsertOrUpdate: isSuccessful = itemsToSynchronize.Count > 0 ? UpdateItems(job, itemsToSynchronize, fieldUpdaters, dynamicItem, context) : ExecuteInsert(job, fieldUpdaters, dynamicItem, context); break; default: throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "Action \"{0}\" is not supported.", job.Action)); } if (isSuccessful) transactionScope.Complete(); return isSuccessful; } } catch (Exception ex) { ++failedAttempts; if (failedAttempts < MaximumAttempts && CanRetry(ex)) { Logger.Log(LogSeverity.Error, "eSync", ex); Logger.Log(LogSeverity.Warning, "eSync", "A recoverable error occurred. The transaction will be rerun."); if (random == null) { random = new Random(Thread.CurrentThread.ManagedThreadId); } var delay = MinimumRetryDelay + TimeSpan.FromSeconds((MaximumRetryDelay - MinimumRetryDelay).TotalSeconds * random.NextDouble()); Thread.Sleep(delay); } else { throw; } } } }
/// <summary> /// Executes the insert. /// </summary> /// <param name="job">The job.</param> /// <param name="fieldUpdaters">The field updaters.</param> /// <param name="dynamicItem">The dynamic item.</param> /// <param name="context">The context.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> private bool ExecuteInsert( IProcessSynchronizer job, IEnumerable<IFieldUpdater> fieldUpdaters, ISyncDynamicItem dynamicItem, SynchronizationContext context) { try { var editItem = DynamicTypeManager.NewEditableRoot<IEditableRoot>(job.ProcessName); editItem.AllowLazyLoading = true; foreach (var fieldUpdater in fieldUpdaters) { fieldUpdater.Update(dynamicItem, editItem); if (fieldUpdater.IsKey) { editItem.KeyFields.Add(fieldUpdater.FieldName); } } if (!((ITrackStatus)editItem).IsValid) { AddValidationErrors(editItem, context); return false; } ((ISavable)editItem).Save(); return true; } catch (Exception ex) { context.AddException(ex); } return false; }
/// <summary> /// Creates the key filter builders. /// </summary> /// <param name="job">The job.</param> /// <returns>List{IFieldFilterBuilder}.</returns> /// <exception cref="Cebos.Veyron.SharedTypes.VeyronException">Invalid mapping</exception> private List<IFieldFilterBuilder> CreateKeyFilterBuilders(IProcessSynchronizer job) { var filterBuilders = new List<IFieldFilterBuilder>(); try { foreach (var field in job.SynchronizationMap.SynchronizedFields.Where(f => f.IsKey)) { var builder = FilterBuilderFactory.CreateFilterBuilder(job.ProcessName, field, job.ValueCalculator); filterBuilders.Add(builder); } } catch (Exception ex) { throw new VeyronException("Invalid mapping", ex); } return filterBuilders; }
/// <summary> /// Creates the field updaters. /// </summary> /// <param name="job">The job.</param> /// <returns>List{IFieldUpdater}.</returns> /// <exception cref="Cebos.Veyron.SharedTypes.VeyronException">Invalid mapping</exception> private List<IFieldUpdater> CreateFieldUpdaters(IProcessSynchronizer job) { var fieldUpdaters = new List<IFieldUpdater>(); try { fieldUpdaters.AddRange( job.SynchronizationMap.SynchronizedFields.Select( synchronizedField => FieldUpdaterFactory.CreateFieldUpdater(job.ProcessName, synchronizedField, job.ValueCalculator))); } catch (Exception ex) { throw new VeyronException("Invalid mapping", ex); } return fieldUpdaters; }
/// <summary> /// Initializes the <see cref="EngineCore"/>. /// </summary> /// <param name="processSynchronizer"> /// The <see cref="IProcessSynchronizer"/> to use for communication with the host. /// </param> public static void Initialize(IProcessSynchronizer processSynchronizer) { lock (_syncRoot) { if (_initialized) return; // Initialize variables. _currentProcessId = RemoteHooking.GetCurrentProcessId(); _serverReporter = processSynchronizer; _logBus = new LogBus(processSynchronizer); #if !SYNCLOG _logBus.Enabled = true; #endif _engine = VirtualizationEngine.InitializeEngine(processSynchronizer); // Attach ProcessExit event handler. AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit; // Mark instance as initialized. _initialized = true; Log.Message("Successfully initialized core components."); } }
/// <summary> /// Updates the job. /// </summary> /// <param name="synchronizer">The synchronizer.</param> public void UpdateJob(IProcessSynchronizer synchronizer) { CheckInitialized(); var jobDetail = GetJobDetail(synchronizer); var triggers = Scheduler.GetTriggersOfJob(jobDetail.Name, QuartzGroupName); // Remove obsolete triggers. foreach (var trigger in triggers) { var esyncTrigger = trigger as ESyncTrigger; if (esyncTrigger == null) { Scheduler.UnscheduleJob(trigger.Name, QuartzGroupName); continue; } if (synchronizer == null) continue; var schedule = synchronizer.Schedules.FirstOrDefault(s => GetTriggerName(s) == esyncTrigger.Name); // If schedule was deleted, is inactive or changed, delete existing trigger. if (schedule == null || !schedule.IsActive || !schedule.Equals(esyncTrigger.Schedule)) Scheduler.UnscheduleJob(trigger.Name, QuartzGroupName); } triggers = Scheduler.GetTriggersOfJob(jobDetail.Name, QuartzGroupName); // Schedule job with new triggers. if (synchronizer == null) return; foreach (var schedule in synchronizer.Schedules.Where(s => s.IsActive)) { try { // Find existing trigger. var trigger = triggers.FirstOrDefault(t => t.Name == GetTriggerName(schedule)); if (trigger != null) continue; trigger = GetTrigger(schedule, jobDetail); if (trigger == null) continue; var calendar = (ICalendar)null; if (trigger.CalendarName != null) calendar = Scheduler.GetCalendar(trigger.CalendarName); var firstFireTime = trigger.ComputeFirstFireTimeUtc(calendar); if (!firstFireTime.HasValue) continue; Scheduler.ScheduleJob(trigger); } catch (Exception ex) { Logger.Log(LogSeverity.Error, GetType().ToString(), ex); } } }
/// <summary> /// Gets the job detail. /// </summary> /// <param name="synchronizer">The synchronizer.</param> /// <returns>JobDetail.</returns> private JobDetail GetJobDetail(IProcessSynchronizer synchronizer) { var jobName = GetJobName(synchronizer); var jobDetail = Scheduler.GetJobDetail(jobName, QuartzGroupName); if (jobDetail != null && jobDetail.JobType != typeof(SyncProcessWorker)) { Scheduler.DeleteJob(jobName, QuartzGroupName); jobDetail = null; } if (jobDetail != null) return jobDetail; jobDetail = new JobDetail(jobName, QuartzGroupName, typeof(SyncProcessWorker), false, true, false); jobDetail.JobDataMap["processName"] = synchronizer.ProcessName; jobDetail.JobDataMap["syncProcessGuid"] = synchronizer.Guid; Scheduler.AddJob(jobDetail, true); return jobDetail; }
/// <summary> /// Initializes a new instance of <see cref="PackagingProcess"/>. /// </summary> /// <param name="startInfo"> /// The <see cref="VirtualProcessStartInfo"/> containing the information used to start the process with. /// </param> /// <param name="synchronizer"> /// The <see cref="ProcessSynchronizer"/> to use for data synchronization with the <see cref="VirtualizedProcess"/>. /// </param> private PackagingProcess(VirtualProcessStartInfo startInfo, IProcessSynchronizer synchronizer) : base(startInfo, synchronizer) { }
/// <summary> /// Initializes a new instance of <see cref="ConnectionManager"/>. /// Before the underlying IPC-channel can be used, <see cref="Connect"/> must be called. /// </summary> /// <param name="processSynchronizer">The object responsible for the synchronization between the server process and the virtualized process.</param> public ConnectionManager(IProcessSynchronizer processSynchronizer) { _syncRoot = new object(); _processSynchronizer = processSynchronizer; }
/// <summary> /// Gets the name of the job. /// </summary> /// <param name="synchronizer">The synchronizer.</param> /// <returns>System.String.</returns> private static string GetJobName(IProcessSynchronizer synchronizer) { return GetJobName(synchronizer.Guid); }