/// <summary> /// Associates the service handler with a message router by registering /// the necessary application message handlers. /// </summary> /// <param name="router">The message router.</param> /// <param name="keyPrefix">The configuration key prefix or (null to use <b>LillTek.Datacenter.AppStore</b>).</param> /// <param name="perfCounters">The application's performance counter set (or <c>null</c>).</param> /// <param name="perfPrefix">The string to prefix any performance counter names (or <c>null</c>).</param> /// <remarks> /// <para> /// Applications that expose performance counters will pass a non-<c>null</c> <b>perfCounters</b> /// instance. The service handler should add any counters it implements to this set. /// If <paramref name="perfPrefix" /> is not <c>null</c> then any counters added should prefix their /// names with this parameter. /// </para> /// </remarks> public void Start(MsgRouter router, string keyPrefix, PerfCounterSet perfCounters, string perfPrefix) { var config = new Config(keyPrefix != null ? keyPrefix : ConfigPrefix); // Make sure the syncLock is set early. this.syncLock = router.SyncRoot; // Make sure that the LillTek.Datacenter message types have been // registered with the LillTek.Messaging subsystem. LillTek.Datacenter.Global.RegisterMsgTypes(); // Verify the router parameter if (router == null) { throw new ArgumentNullException("router", "Router cannot be null."); } if (this.router != null) { throw new InvalidOperationException("This handler has already been started."); } // General initialization mode = config.Get <AppStoreMode>("Mode", AppStoreMode.Primary); primaryBroadcast = config.Get("PrimaryBroadcast", true); packageScanInterval = config.Get("PackageScanInterval", TimeSpan.FromMinutes(5)); primaryPollInterval = config.Get("PrimaryPollInterval", TimeSpan.FromMinutes(15)); primaryPollTime = SysTime.Now; onTransfer = new AsyncCallback(OnTransfer); downloads = new Dictionary <AppRef, PendingDownload>(); forceSync = false; cDownloads = 0; netFail = false; // Initialize the package folder packageFolder = new AppPackageFolder(syncLock, config.Get("PackageFolder", "Packages")); packageFolder.ChangeEvent += new MethodArg1Invoker(OnPackageFolderChange); packageScanTime = SysTime.Now; // Initialize the performance counters startTime = DateTime.UtcNow; perf = new Perf(perfCounters, perfPrefix); // Crank up the background task timer. bkTimer = new GatedTimer(new TimerCallback(OnBkTimer), null, config.Get("BkTaskInterval", TimeSpan.FromSeconds(1))); try { // Initialize the router this.router = router; // Join the cluster, initializing this instance's state. cluster = new ClusterMember(router, ClusterMemberSettings.LoadConfig(config.KeyPrefix + "Cluster")); cluster["Mode"] = this.mode.ToString(); cluster.ClusterStatusUpdate += new ClusterMemberEventHandler(OnClusterStatusUpdate); cluster.Start(); // Rather than calling cluster.JoinWait() which could take a really long // time, I'm going to sleep for two seconds. There are three scenarios: // // 1. This is the first Application Store instance. // // 2. Other instances are running but they haven't // organized into a cluster. // // 3. A cluster is already running. // // If #1 is the current situation, then it will take a very long time // for JoinWait() to return because we have to go through the entire // missed master broadcast and election periods. Since we're the only // instance, we could have started serving content well before this. // // #2 won't be very common but if it is the case, the worst thing // that will happen is that it will take a while to discover the // primary store. // // If #3 is the case, then two seconds should be long enough for the // master to send the instance a cluster update. Thread.Sleep(2000); // Register the message handlers via the cluster member // so that the endpoint used will be the member's instanceEP. cluster.AddTarget(this, AppStoreHandler.DynamicScope); } catch { if (packageFolder != null) { packageFolder.Dispose(); packageFolder = null; } if (bkTimer != null) { bkTimer.Dispose(); bkTimer = null; } router.Dispatcher.RemoveTarget(this); if (cluster != null) { cluster.Stop(); cluster = null; } throw; } }