/// <summary> /// Returns configuration information formatted as an .ini file as /// specified by <see cref="Config" />. /// </summary> /// <param name="settings">Provider specific settings.</param> /// <param name="cacheFile">File path to use for cached settings.</param> /// <param name="machineName">The requesting machine's name.</param> /// <param name="exeFile">The unqualified name of the current process' executable file.</param> /// <param name="exeVersion">The version number of the current executable file.</param> /// <param name="usage">Used to indicate a non-default usage for this application instance.</param> /// <returns>The configuration file data (or <c>null</c>).</returns> /// <remarks> /// <para> /// The settings parameter must specify the <b>RouterEP</b> and <b>CloudEP</b> setting values, /// (the rest are optional): /// </para> /// <code language="none"> /// RouterEP = <Router Physical Endpoint> /// CloudEP = <IP Endpoint> /// ConfigEP = <logical or physical endpoint> (optional) /// EnableP2P = 0 | 1 (optional) /// </code> /// <para> /// <b>RouterEP</b> specifies the unique physical endpoint to be assigned to the /// bootstrap router. This must be unique across all routers so it's best to /// use the $(Guid) environment variable somewhere within the definition. /// </para> /// <para> /// <b>CloudEP</b> specifies the IP endpoint to be used by a bootstrap /// message router to discover the other routers on the network, and <b>ConfigEP</b> /// specifies the logical or physical messaging endpoint of the configuration service. /// The class uses this information to start a leaf bootstrap router which will /// be used to discover and communicate with the configuration service. /// </para> /// <para> /// The <b>EnableP2P</b> setting indicates whether the bootstrap router should be /// peer-to-peer enabled. This parameter is optional and defaults to "1". This /// setting is present for scalability reasons. P2P enabling the bootstrap router /// will cause a lot of network traffic in environments with a lot of servers since /// every other P2P enabled router will establish a connection to the bootstrap /// router to discover its logical routes. This is a significant overhead for a /// router that exposes no routes. <b>EnableP2P</b> should be set to "0" in production /// environments with a lot of servers and with a hub server present to avoid this /// problem. /// </para> /// <para> /// The cacheFile parameter specifies the name of the file where the provider /// may choose to cache configuration settings. This implementation will save a copy /// of the last set of settings returned by a remote service in this file and /// then use these settings if the remote service is unavailable the next time /// the service is started. This improves the operational robustness of a /// large network of servers. Choosing to implement this behavior is completely /// up to the discretion of the provider. Specify <b>(no-cache)</b> to disable /// caching behavior. /// </para> /// <para> /// Note that the method will return null if the requested configuration /// information could not be loaded for any reason. /// </para> /// <para> /// This class queries the configuration service via the following message /// endpoints: /// </para> /// <code language="none"> /// abstract://LillTek/DataCenter/ConfigService /// </code> /// <para> /// This may be remapped to another logical endpoint via the message /// routers <b>MsgRouter.AbstractMap</b> configuation setting. /// </para> /// </remarks> public string GetConfig(ArgCollection settings, string cacheFile, string machineName, string exeFile, Version exeVersion, string usage) { LeafRouter router = null; RouterSettings rSettings; string s; MsgEP routerEP; IPEndPoint cloudEP; MsgEP configEP; bool enableP2P; GetConfigAck ack; string configText = null; Exception queryException = null; // Make sure that the assembly's message types have been // registered with the LillTek.Messaging subsystem. Global.RegisterMsgTypes(); // Parse the settings s = settings["RouterEP"]; if (s == null) { throw new ArgumentException("[RouterEP] setting expected.", "settings"); } try { routerEP = MsgEP.Parse(s); } catch { throw new ArgumentException("[RouterEP] is invalid.", "settings"); } if (!routerEP.IsPhysical) { throw new ArgumentException("[RouterEP] must specify a physical endpoint.", "settings"); } s = settings["CloudEP"]; if (s == null) { throw new ArgumentException("[CloudEP] setting expected.", "settings"); } cloudEP = Serialize.Parse(s, new IPEndPoint(IPAddress.Any, 0)); if (cloudEP == new IPEndPoint(IPAddress.Any, 0)) { throw new ArgumentException("[CloudEP] setting is invalid.", "settings"); } configEP = Serialize.Parse(settings["ConfigEP"], GetConfigEP); enableP2P = Serialize.Parse(settings["EnableP2P"], true); // Crank up a bootstrap leaf router and perform the query. try { router = new LeafRouter(); rSettings = new RouterSettings(routerEP); rSettings.AppName = rSettings.AppName + " (config boot)"; rSettings.CloudEP = cloudEP; rSettings.EnableP2P = enableP2P; router.Start(rSettings); ack = (GetConfigAck)router.Query(configEP, new GetConfigMsg(machineName, exeFile, exeVersion, usage)); configText = ack.ConfigText; } catch (Exception e) { queryException = e; configText = null; } finally { if (router != null) { router.Stop(); } } if (configText != null) { // Cache the query result if requested if (!string.IsNullOrWhiteSpace(cacheFile) && String.Compare(cacheFile, "(no-cache)", true) != 0) { StreamWriter writer; try { writer = new StreamWriter(cacheFile); writer.Write(configText); writer.Close(); } catch { } } } else { // Try loading a cached configuration if (string.IsNullOrWhiteSpace(cacheFile)) { SysLog.LogException(queryException, "GetConfig query failed with no cached settings."); } else { StreamReader reader; SysLog.LogWarning("GetConfig query failed: Loading cached settings."); try { reader = new StreamReader(cacheFile); configText = reader.ReadToEnd(); reader.Close(); } catch { configText = null; } } } return(configText); }