using System; using System.Collections; using System.Data; using System.Data.SqlClient; using System.Diagnostics; using System.Runtime.InteropServices; using System.Security.Principal; using System.Threading; using Microsoft.Win32; namespace UDDI { /// **************************************************************** /// public class Config /// ---------------------------------------------------------------- /// /// Manages configuration settings for UDDI. /// /// ---------------------------------------------------------------- /// /// The configuration table is of very high importance, so if /// we fail to retrieve the configuration settings we'll mark /// the table as being invalid by setting it to NULL. Any /// attempt to retrieve a setting while the table is in this /// invalid state will result in an exception. /// /// **************************************************************** /// // // TODO: Get and Set static methods should check the IsValid property through a // common piece of code method. This would avoid a significant // amount of code bload in the try/catch blocks. // public class Config { private const string registryRoot = @"SOFTWARE\Microsoft\UDDI"; private static Hashtable settings = null; private static ReaderWriterLock readWriteLock = new ReaderWriterLock(); private static Thread monitor = null; private static WindowsIdentity identity = null; public static Exception LastError = null; public delegate void ConfigChangeHandler(); public static event ConfigChangeHandler ConfigChange; /// **************************************************************** /// private Config /// ---------------------------------------------------------------- /// /// Constructor. /// /// **************************************************************** /// static Config() { // // 730294 - Never throw exceptions out of this static initializer // try { System.Diagnostics.Debug.Write( "INFO CONF Configuration manager starting (thread=" + Thread.CurrentThread.GetHashCode().ToString() + ", user='" + WindowsIdentity.GetCurrent().Name + "')\r\n" ); // // Initialize the settings collection. // Refresh(); // // Verify that the version of the database that we are using is compatible. // string versionRegKeyName = "Setup.WebServer.DBSchemaVersion"; // // This key will not exist if only the DB was installed on this machine. In that case, // default to the DbServer.DBSchemaVersion setting. // if( false == Config.SettingExists( versionRegKeyName ) ) { versionRegKeyName = "Setup.DbServer.DBSchemaVersion"; } UDDI.Diagnostics.Debug.VerifySetting( versionRegKeyName ); UDDI.Diagnostics.Debug.VerifySetting( "Database.Version" ); Version webServerVersion = new Version( Config.GetString( versionRegKeyName ) ); Version dataBaseVersion = new Version( Config.GetString( "Database.Version" ) ); // // The major and minor versions must be equal. // if( ( dataBaseVersion.Major != webServerVersion.Major ) || ( dataBaseVersion.Minor != webServerVersion.Minor ) ) { UDDIText errorMessage = new UDDIText( "UDDI_ERROR_SCHEMA_MISMATCH", webServerVersion.ToString(), dataBaseVersion.ToString() ); throw new UDDIException( ErrorType.E_fatalError, errorMessage ); } // // Install the registry change notification event handler. By // marking this thread as a background thread, the runtime will // automatically terminate it when all foreground threads have // finished processing. This eliminates the need for a separate // shutdown mechanism. // identity = WindowsIdentity.GetCurrent(); monitor = new Thread( new ThreadStart( Registry_OnChange ) ); monitor.IsBackground = true; monitor.Start(); } catch( UDDIException uddiException ) { // // Something has thrown a UDDIException; in this case, just make this exception the last error. // LastError = uddiException; OperatorMessage( UDDI.Diagnostics.OperatorMessageType.ConfigError, uddiException.Message ); // // If any exception occurs, invalidate ourselves. // InvalidateSelf(); } catch( Exception ) { // // Something has thrown a generic Exception; in this case, just create a generic UDDIException, and make // that the last error. // UDDIText errorMessage = new UDDIText( "UDDI_ERROR_CONFIG_COMMUNICATION_ERROR" ); LastError = new UDDIException( ErrorType.E_fatalError, errorMessage ); OperatorMessage( UDDI.Diagnostics.OperatorMessageType.CannotReadSettings, errorMessage.GetText() ); // // If any exception occurs, invalidate ourselves. // InvalidateSelf(); } } /// **************************************************************** /// public CheckForUpdate [static] /// ---------------------------------------------------------------- /// /// /// **************************************************************** /// public static void CheckForUpdate() { // // Throw an exception if we are not in a valid state. // CheckIsValid(); SqlConnection conn = new SqlConnection( Config.GetString( "Database.WriterConnectionString" ) ); SqlCommand cmd = new SqlCommand( "net_config_getLastChangeDate", conn ); conn.Open(); try { cmd.CommandType = CommandType.StoredProcedure; string lastChange = (string)cmd.ExecuteScalar(); string lastRefresh = Config.GetString( "LastChange", "Jan 01 0001 12:00AM" ); // // Compare the database last change configuration with our latest // config. If it differs, the database has changed and we need // to refresh. Use the minimum SQL date value if the config value // isn't available. // if( 0 != String.Compare( lastChange, lastRefresh ) ) Config.Refresh(); } finally { conn.Close(); } } /// **************************************************************** /// public IsValid [static] /// ---------------------------------------------------------------- /// /// Returns true if the config table is valid (i.e. has been /// properly initialized). /// /// ---------------------------------------------------------------- /// /// True if the table is valid, false otherwise. /// /// **************************************************************** /// public static bool IsValid { get { readWriteLock.AcquireReaderLock( Constants.ReadLockTimeout ); try { return ( null != settings ); } finally { readWriteLock.ReleaseReaderLock(); } } } /// **************************************************************** /// private OperatorMessage [static] /// ---------------------------------------------------------------- /// /// Writes a message to the event log. /// /// ---------------------------------------------------------------- /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// ---------------------------------------------------------------- /// /// This is a safe version of the Debug.WriteEventLog method /// that prevents re-entrancy issues with the config. /// /// **************************************************************** /// private static void OperatorMessage( UDDI.Diagnostics.OperatorMessageType messageType, string message ) { try { EventLog.WriteEntry( "UDDIRuntime", message, EventLogEntryType.Error, (int)messageType, (int)UDDI.Diagnostics.CategoryType.Config ); } catch( Exception ) { Debug.Write( "ERRO CONF Error writing message to event log.\r\n\r\n" + "Message:\r\n" + message ); } } /// **************************************************************** /// private AddSetting [static] /// ---------------------------------------------------------------- /// /// Adds a configuration setting. /// /// ---------------------------------------------------------------- /// /// The setting key. /// /// /// /// The configuration setting data. /// /// ---------------------------------------------------------------- /// /// This function assumes that the writer lock has already been /// obtained. /// /// **************************************************************** /// private static void AddSetting( string key, object data ) { // // Throw an exception if we are not in a valid state. // CheckIsValid(); if( true == settings.ContainsKey( key ) ) { Debug.Write( "INFO CONF Updating setting '" + key + "' value '" + data.ToString() + "'\r\n" ); settings[ key ] = data; } else { Debug.Write( "INFO CONF Adding setting '" + key + "' value '" + data.ToString() + "'\r\n" ); settings.Add( key, data ); } } /// **************************************************************** /// private SetString [static] /// ---------------------------------------------------------------- /// /// Adds a configuration setting to the database configuration table. /// /// ---------------------------------------------------------------- /// /// The configuration setting name. /// /// /// /// The value to store in the configuration. /// /// ---------------------------------------------------------------- /// /// This function does not update the in memory cache. /// /// **************************************************************** /// public static void SetString( string name, string value ) { // // Throw an exception if we are not in a valid state. // CheckIsValid(); // // Connect to the database and get the configuration settings. // SqlConnection conn = new SqlConnection( Config.GetString( "Database.WriterConnectionString" ) ); SqlCommand cmd = new SqlCommand( "net_config_save", conn ); conn.Open(); try { cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add( new SqlParameter( "@configName", SqlDbType.NVarChar, UDDI.Constants.Lengths.ConfigName ) ).Direction = ParameterDirection.Input; cmd.Parameters.Add( new SqlParameter( "@configValue", SqlDbType.NVarChar, UDDI.Constants.Lengths.ConfigValue ) ).Direction = ParameterDirection.Input; cmd.Parameters[ "@configName" ].Value = name; cmd.Parameters[ "@configValue" ].Value = value; cmd.ExecuteNonQuery(); } finally { conn.Close(); } } public static int GetCount() { // // Throw an exception if we are not in a valid state. // CheckIsValid(); int n = 0; // // Acquire the reader lock and read the setting. // readWriteLock.AcquireReaderLock( Constants.ReadLockTimeout ); try { n = settings.Count; } finally { readWriteLock.ReleaseReaderLock(); } return n; } /// **************************************************************** /// private CopyTo [static] /// ---------------------------------------------------------------- /// /// Adds a configuration setting. /// /// ---------------------------------------------------------------- /// /// The array to copy the stuff into. /// /// /// /// The location in the array to start copying the stuff into. /// /// ---------------------------------------------------------------- /// /// This function assumes that the writer lock has already been /// obtained. /// /// **************************************************************** /// public static void CopyTo( Array array, int arrayIndex ) { // // Throw an exception if we are not in a valid state. // CheckIsValid(); // // Acquire the reader lock and read the setting. // readWriteLock.AcquireReaderLock( Constants.ReadLockTimeout ); try { settings.CopyTo( array, arrayIndex ); } finally { readWriteLock.ReleaseReaderLock(); } } /// **************************************************************** /// public GetObject [static] /// ---------------------------------------------------------------- /// /// Retrieves the setting with the given key. /// /// ---------------------------------------------------------------- /// /// The setting key. /// /// ---------------------------------------------------------------- /// /// The value of the key, if it exists. An exception is raised /// if it does not. /// /// **************************************************************** /// public static object GetObject( string key ) { // // Throw an exception if we are not in a valid state. // CheckIsValid(); object setting = null; try { // // Acquire the reader lock and read the setting. // readWriteLock.AcquireReaderLock( Constants.ReadLockTimeout ); setting = settings[ key ]; } catch( NullReferenceException ) { // // Null reference exceptions are generated when the settings // table could not be read. // #if never OperatorMessage( UDDI.Diagnostics.OperatorMessageType.ConfigInvalid, "Configuration table is in an invalid state.\r\n\r\n" + LastError.ToString() ); #endif UDDIText operatorMessage = new UDDIText( "UDDI_ERROR_OPERATOR_MESSAGE_CONFIG_INVALID_STATE", LastError.ToString() ); OperatorMessage( UDDI.Diagnostics.OperatorMessageType.ConfigInvalid, operatorMessage.GetText() ); #if never throw new UDDIException( ErrorType.E_fatalError, "Server configuration error.\r\n\r\n" + LastError.ToString() ); #endif throw new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_CONFIG_COMMUNICATION_ERROR" ); } finally { // // Release the reader lock. // readWriteLock.ReleaseReaderLock(); } return setting; } /// **************************************************************** /// public GetObject [static] /// ---------------------------------------------------------------- /// /// Retrieves the setting with the given key. /// /// ---------------------------------------------------------------- /// /// The setting key. /// /// /// /// Default value to use if the key does not exist. /// /// ---------------------------------------------------------------- /// /// The value of the key, if it exists, or the specified default /// value if it does not. /// /// **************************************************************** /// public static object GetObject( string key, object defaultValue ) { // // Throw an exception if we are not in a valid state. // CheckIsValid(); object setting = defaultValue; try { // // Acquire the reader lock and read the setting. // readWriteLock.AcquireReaderLock( Constants.ReadLockTimeout ); if( settings.ContainsKey( key ) ) setting = settings[ key ]; } catch( NullReferenceException ) { // // Null reference exceptions are generated when the settings // table could not be read. // #if never OperatorMessage( UDDI.Diagnostics.OperatorMessageType.ConfigInvalid, "Configuration table is in an invalid state.\r\n\r\n" + LastError.ToString() ); #endif UDDIText operatorMessage = new UDDIText( "UDDI_ERROR_OPERATOR_MESSAGE_CONFIG_INVALID_STATE", LastError.ToString() ); OperatorMessage( UDDI.Diagnostics.OperatorMessageType.ConfigInvalid, operatorMessage.GetText() ); #if never throw new UDDIException( ErrorType.E_fatalError, "Server configuration error.\r\n\r\n" + LastError.ToString() ); #endif throw new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_CONFIG_COMMUNICATION_ERROR" ); } finally { // // Release the reader lock. // readWriteLock.ReleaseReaderLock(); } return setting; } /// **************************************************************** /// public GetInt [static] /// ---------------------------------------------------------------- /// /// Retrieves the setting with the given key. /// /// ---------------------------------------------------------------- /// /// The setting key. /// /// ---------------------------------------------------------------- /// /// The value of the key, if it exists. An exception is raised /// if it does not. /// /// **************************************************************** /// public static string GetString( string key ) { // // Throw an exception if we are not in a valid state. // CheckIsValid(); string setting = null; try { // // Acquire the reader lock and read the setting. // readWriteLock.AcquireReaderLock( Constants.ReadLockTimeout ); object data = settings[ key ]; if( data is System.String ) setting = (string)data; else setting = Convert.ToString( data ); } catch( NullReferenceException ) { // // Null reference exceptions are generated when the settings // table could not be read. // #if never OperatorMessage( UDDI.Diagnostics.OperatorMessageType.ConfigInvalid, "Configuration table is in an invalid state.\r\n\r\n" + LastError.ToString() ); #endif UDDIText operatorMessage = new UDDIText( "UDDI_ERROR_OPERATOR_MESSAGE_CONFIG_INVALID_STATE", LastError.ToString() ); OperatorMessage( UDDI.Diagnostics.OperatorMessageType.ConfigInvalid, operatorMessage.GetText() ); #if never throw new UDDIException( ErrorType.E_fatalError, "Server configuration error.\r\n\r\n" + LastError.ToString() ); #endif throw new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_CONFIG_COMMUNICATION_ERROR" ); } finally { // // Release the reader lock. // readWriteLock.ReleaseReaderLock(); } return setting; } /// **************************************************************** /// public GetString [static] /// ---------------------------------------------------------------- /// /// Retrieves the setting with the given key. /// /// ---------------------------------------------------------------- /// /// The setting key. /// /// /// /// Default value to use if the key does not exist. /// /// ---------------------------------------------------------------- /// /// The value of the key, if it exists. Otherwise returns the /// default value. /// /// **************************************************************** /// public static string GetString( string key, string defaultValue ) { // // Throw an exception if we are not in a valid state. // CheckIsValid(); string setting = defaultValue; try { // // Acquire the reader lock and read the setting. // readWriteLock.AcquireReaderLock( Constants.ReadLockTimeout ); if( settings.ContainsKey( key ) ) { object data = settings[ key ]; if( data is System.String ) setting = (string)data; else setting = Convert.ToString( data ); } } catch( NullReferenceException ) { // // Null reference exceptions are generated when the settings // table could not be read. // #if never OperatorMessage( UDDI.Diagnostics.OperatorMessageType.ConfigInvalid, "Configuration table is in an invalid state.\r\n\r\n" + LastError.ToString() ); #endif UDDIText operatorMessage = new UDDIText( "UDDI_ERROR_OPERATOR_MESSAGE_CONFIG_INVALID_STATE", LastError.ToString() ); OperatorMessage( UDDI.Diagnostics.OperatorMessageType.ConfigInvalid, operatorMessage.GetText() ); #if never throw new UDDIException( ErrorType.E_fatalError, "Server configuration error.\r\n\r\n" + LastError.ToString() ); #endif throw new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_CONFIG_COMMUNICATION_ERROR" ); } finally { // // Release the reader lock. // readWriteLock.ReleaseReaderLock(); } return setting; } /// **************************************************************** /// public GetInt [static] /// ---------------------------------------------------------------- /// /// Retrieves the setting with the given key. /// /// ---------------------------------------------------------------- /// /// The setting key. /// /// ---------------------------------------------------------------- /// /// The value of the key. /// /// **************************************************************** /// public static int GetInt( string key ) { // // Throw an exception if we are not in a valid state. // CheckIsValid(); int setting = 0; try { // // Acquire the reader lock and read the setting. // readWriteLock.AcquireReaderLock( Constants.ReadLockTimeout ); object data = settings[ key ]; if( data is System.Int32 ) setting = (int)data; else setting = Convert.ToInt32( data ); } catch( NullReferenceException ) { // // Null reference exceptions are generated when the settings // table could not be read. // #if never OperatorMessage( UDDI.Diagnostics.OperatorMessageType.ConfigInvalid, "Configuration table is in an invalid state.\r\n\r\n" + LastError.ToString() ); #endif UDDIText operatorMessage = new UDDIText( "UDDI_ERROR_OPERATOR_MESSAGE_CONFIG_INVALID_STATE", LastError.ToString() ); OperatorMessage( UDDI.Diagnostics.OperatorMessageType.ConfigInvalid, operatorMessage.GetText() ); #if never throw new UDDIException( ErrorType.E_fatalError, "Server configuration error.\r\n\r\n" + LastError.ToString() ); #endif throw new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_CONFIG_COMMUNICATION_ERROR" ); } finally { // // Release the reader lock. // readWriteLock.ReleaseReaderLock(); } return setting; } /// **************************************************************** /// public GetInt [static] /// ---------------------------------------------------------------- /// /// Retrieves the setting with the given key. /// /// ---------------------------------------------------------------- /// /// The setting key. /// /// /// /// Default value to use if the key does not exist. /// /// ---------------------------------------------------------------- /// /// The value of the key, if it exists. Otherwise returns the /// default value. /// /// **************************************************************** /// public static int GetInt( string key, int defaultValue ) { // // Throw an exception if we are not in a valid state. // CheckIsValid(); int setting = defaultValue; try { // // Acquire the reader lock and read the setting. // readWriteLock.AcquireReaderLock( Constants.ReadLockTimeout ); if( settings.ContainsKey( key ) ) { object data = settings[ key ]; if( data is System.Int32 ) setting = (int)data; else setting = Convert.ToInt32( data ); } } catch( NullReferenceException ) { // // Null reference exceptions are generated when the settings // table could not be read. // #if never OperatorMessage( UDDI.Diagnostics.OperatorMessageType.ConfigInvalid, "Configuration table is in an invalid state.\r\n\r\n" + LastError.ToString() ); #endif UDDIText operatorMessage = new UDDIText( "UDDI_ERROR_OPERATOR_MESSAGE_CONFIG_INVALID_STATE", LastError.ToString() ); OperatorMessage( UDDI.Diagnostics.OperatorMessageType.ConfigInvalid, operatorMessage.GetText() ); #if never throw new UDDIException( ErrorType.E_fatalError, "Server configuration error.\r\n\r\n" + LastError.ToString() ); #endif throw new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_CONFIG_COMMUNICATION_ERROR" ); } finally { // // Release the reader lock. // readWriteLock.ReleaseReaderLock(); } return setting; } /// **************************************************************** /// public SettingExists [static] /// ---------------------------------------------------------------- /// /// Determines whether the given setting exists in the /// collection. /// /// ---------------------------------------------------------------- /// /// The setting key. /// /// ---------------------------------------------------------------- /// /// Returns true if the setting exists. /// /// **************************************************************** /// public static bool SettingExists( string key ) { // // Throw an exception if we are not in a valid state. // CheckIsValid(); bool exists = false; try { // // Acquire the reader lock and read the setting. // readWriteLock.AcquireReaderLock( Constants.ReadLockTimeout ); exists = settings.ContainsKey( key ); } catch( NullReferenceException ) { // // Null reference exceptions are generated when the settings // table could not be read. // #if never OperatorMessage( UDDI.Diagnostics.OperatorMessageType.ConfigInvalid, "Configuration table is in an invalid state.\r\n\r\n" + LastError.ToString() ); #endif UDDIText operatorMessage = new UDDIText( "UDDI_ERROR_OPERATOR_MESSAGE_CONFIG_INVALID_STATE", LastError.ToString() ); OperatorMessage( UDDI.Diagnostics.OperatorMessageType.ConfigInvalid, operatorMessage.GetText() ); #if never throw new UDDIException( ErrorType.E_fatalError, "Server configuration error.\r\n\r\n" + LastError.ToString() ); #endif throw new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_CONFIG_COMMUNICATION_ERROR" ); } finally { // // Release the reader lock. // readWriteLock.ReleaseReaderLock(); } return exists; } /// **************************************************************** /// public Refresh [static] /// ---------------------------------------------------------------- /// /// Refreshes setting information. /// /// **************************************************************** /// public static void Refresh() { try { Debug.Write( "INFO CONF Refreshing configuration settings (thread=" + Thread.CurrentThread.GetHashCode().ToString() + ", user='" + WindowsIdentity.GetCurrent().Name + "')\r\n" ); // // Obtain the writer lock. // readWriteLock.AcquireWriterLock( Timeout.Infinite ); // // Clear the existing setting table. // settings = new Hashtable( CaseInsensitiveHashCodeProvider.Default, CaseInsensitiveComparer.Default ); // // Attempt to get the database connection string. // RegistryKey databaseKey = Registry.LocalMachine.OpenSubKey( registryRoot + @"\Database" ); string writerConnectionString = null; if( null != databaseKey ) { try { writerConnectionString = (string)databaseKey.GetValue( "WriterConnectionString" ); } catch( Exception ) { throw new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_CONFIG_DB_WRITER_CONNECTION_STRING" ); } } // // 730294 - The MMC will set this value to "", so use StringEmtpy, not just check for null. // if( true == Utility.StringEmpty( writerConnectionString ) ) { throw new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_CONFIG_DB_WRITER_CONNECTION_STRING" ); } // // Connect to the database and get the configuration settings. // SqlConnection conn = new SqlConnection( writerConnectionString ); SqlCommand cmd = new SqlCommand( "net_config_get", conn ); conn.Open(); try { cmd.CommandType = CommandType.StoredProcedure; SqlDataReader reader = cmd.ExecuteReader(); while( reader.Read() ) { // // We'll treat all database settings as strings, except for those // with special prefixes. // string keyName = reader.GetString( 0 ); string keyPrefix = ""; int separator = keyName.IndexOf( "." ); if( -1 != separator ) keyPrefix = keyName.Substring( 0, separator ); if( "Length" == keyPrefix ) { AddSetting( keyName, Convert.ToInt32( reader.GetString( 1 ) ) ); } else { AddSetting( keyName, reader.GetString( 1 ) ); } } } finally { conn.Close(); } // // Read configuration data from the registry. // RegistryKey root = Registry.LocalMachine.OpenSubKey( registryRoot ); if( null != root ) RefreshRegistrySettings( root, "", true ); } catch( SqlException e ) { LastError = e; UDDIText operatorMessage = new UDDIText( "UDDI_ERROR_OPERATOR_MESSAGE_ERROR_READING_CONFIG_SETTINGS", e.ToString() ); OperatorMessage( UDDI.Diagnostics.OperatorMessageType.CannotReadSettings, operatorMessage.GetText() ); // // Mark the table as being invalid. // settings = null; } catch( Exception e ) { LastError = e; UDDIText operatorMessage = new UDDIText( "UDDI_ERROR_OPERATOR_MESSAGE_ERROR_READING_CONFIG_SETTINGS", e.ToString() ); OperatorMessage( UDDI.Diagnostics.OperatorMessageType.CannotReadSettings, operatorMessage.GetText() ); // // Mark the table as being invalid. // settings = null; } finally { // // Release the writer lock. // readWriteLock.ReleaseWriterLock(); if( null != ConfigChange ) { try { ConfigChange(); } catch( Exception ) { } } } } /// **************************************************************** /// private RefreshRegistrySettings [static] /// ---------------------------------------------------------------- /// /// Refreshes configuration settings from the registry, /// starting at the specified root. /// /// ---------------------------------------------------------------- /// /// Registry key that contains the settings. /// /// /// /// Optional. Prefix to add to the beginning of setting names /// in the collection. /// /// /// /// True if subkeys of the root should be searched. /// /// ---------------------------------------------------------------- /// /// If recursion is enabled, values in subkeys of the root are /// also enumerated. When enumerating subkeys, the value names /// are prefixed with the name of the subkey. /// /// **************************************************************** /// private static void RefreshRegistrySettings( RegistryKey root, string prefix, bool recurse ) { // // Add the values in the given root. // foreach( string name in root.GetValueNames() ) AddSetting( prefix + name, root.GetValue( name ) ); // // If recursion is enabled, search through the subkeys under the given root. The // key will be prefixed with the subkey name. // if( true == recurse ) { foreach( string name in root.GetSubKeyNames() ) RefreshRegistrySettings( root.OpenSubKey( name ), prefix + name + ".", true ); } } /// **************************************************************** /// private Registry_OnChange [static] /// ---------------------------------------------------------------- /// /// Event handler for setting change events in the registry. /// /// **************************************************************** /// private static void Registry_OnChange() { // // Impersonate the identity of the main thread. // if( WindowsIdentity.GetCurrent().Name != identity.Name ) { try { identity.Impersonate(); } catch( Exception e ) { #if never OperatorMessage( UDDI.Diagnostics.OperatorMessageType.CannotReadSettings, "Configuration refresh thread was unable take on the identity " + "of the creating thread (thread=" + Thread.CurrentThread.GetHashCode().ToString() + ").\r\n\r\nDetails:\r\n" + e.ToString() ); #endif UDDIText operatorMessage = new UDDIText( "UDDI_ERROR_OPERATOR_MESSAGE_CANNOT_IMPERSONATE", Thread.CurrentThread.GetHashCode().ToString(), e.ToString() ); OperatorMessage( UDDI.Diagnostics.OperatorMessageType.CannotReadSettings, operatorMessage.GetText() ); return; } } Debug.Write( "INFO CONF Configuration registry change handler starting (thread=" + Thread.CurrentThread.GetHashCode().ToString() + ", user='" + WindowsIdentity.GetCurrent().Name + "')\r\n" ); // // Obtain a handle to the registry key we want to monitor. // uint hkey; int hr = Win32.RegOpenKeyEx( Win32.HKEY_LOCAL_MACHINE, registryRoot, 0, Win32.KEY_READ, out hkey ); if( 0 != hr ) { #if never OperatorMessage( UDDI.Diagnostics.OperatorMessageType.CannotReadSettings, "Unable to access registry root '" + registryRoot + "'" ); #endif UDDIText operatorMessage = new UDDIText( "UDDI_ERROR_OPERATOR_MESSAGE_UNABLE_TO_ACCESS_REGISTRY", registryRoot ); OperatorMessage( UDDI.Diagnostics.OperatorMessageType.CannotReadSettings, operatorMessage.GetText() ); } // // Monitor changes to the registry key. // AutoResetEvent changeEvent = new AutoResetEvent( false ); while( true ) { // // Wait for a change notification event. // Win32.RegNotifyChangeKeyValue( hkey, true, Win32.REG_NOTIFY_CHANGE_LAST_SET, (uint)changeEvent.Handle.ToInt32(), true ); changeEvent.WaitOne(); // // Refresh the settings. We trap all errors since we never // want to kill the refresh thread, otherwise who would read // the settings when they finally become available? // try { Refresh(); } catch( Exception e ) { #if never OperatorMessage( UDDI.Diagnostics.OperatorMessageType.CannotReadSettings, "Unable to refresh configuration settings from registry monitor thread.\r\n\r\nDetails:\r\n" + e.ToString() ); #endif UDDIText operatorMessage = new UDDIText( "UDDI_ERROR_OPERATOR_MESSAGE_UNABLE_TO_REFRESH", e.ToString() ); OperatorMessage( UDDI.Diagnostics.OperatorMessageType.CannotReadSettings, operatorMessage.GetText() ); } } } // // 730294 - Centralize what exceptions we throw. // private static void CheckIsValid() { if( !IsValid ) { // // Make sure we are always throwing a nice UDDIException; construct a generic one if you have to. // UDDIException uddiException = LastError as UDDIException; if( null == uddiException ) { uddiException = new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_CONFIG_COMMUNICATION_ERROR" ); } throw uddiException; } } // // 730294 - Clean up the way we do exceptions. // private static void InvalidateSelf() { // // Make ourself invalid by nulling out the setting table. Make sure to keep this in sync with // what the IsValid property is doing. // try { readWriteLock.AcquireReaderLock( Constants.ReadLockTimeout ); settings = null; } finally { readWriteLock.ReleaseReaderLock(); } } } /// ******************************************************************** /// public class CachedSetting /// -------------------------------------------------------------------- /// /// /// ******************************************************************** /// public class CachedSetting { private ReaderWriterLock readWriteLock = new ReaderWriterLock(); // // 730294 - Keep the state of a CachedSetting in sync with the Config by just using the Config.IsValid property instead // of keeping a valid member. Remove the valid member. // private string key; private object setting; private object defaultValue; private bool exists; private bool defaultSpecified; public event Config.ConfigChangeHandler ConfigChange; /// **************************************************************** /// public CachedSetting [constructor] /// ---------------------------------------------------------------- /// /// /// ---------------------------------------------------------------- /// /// /// **************************************************************** /// public CachedSetting( string key ) { this.key = key; this.defaultSpecified = false; this.defaultValue = null; Config_OnChange(); Config.ConfigChange += new Config.ConfigChangeHandler( Config_OnChange ); } /// **************************************************************** /// public CachedSetting [constructor] /// ---------------------------------------------------------------- /// /// /// ---------------------------------------------------------------- /// /// /// /// /// /// **************************************************************** /// public CachedSetting( string key, object defaultValue ) { this.key = key; this.defaultSpecified = true; this.defaultValue = defaultValue; Config_OnChange(); Config.ConfigChange += new Config.ConfigChangeHandler( Config_OnChange ); } /// **************************************************************** /// private Config_OnChange [event handler] /// ---------------------------------------------------------------- /// /// /// ---------------------------------------------------------------- /// /// We don't have to worry about acquiring a reader lock in /// this method because the configuration update only executes /// on a single background thread. /// /// **************************************************************** /// private void Config_OnChange() { // // Make sure the config is valid. If not, just return. // if( !Config.IsValid ) { return; } object setting = null; // // Retrieve the new setting. // bool exists = Config.SettingExists( key ); if( exists ) setting = Config.GetObject( key ); // // Check to see if we need to do anything. First of all, if the // existance of the setting has changed, we need to update that // information. If that has not changed, then the only other way // we do not have to update is if the setting itself hasn't // changed. // if( exists == this.exists && null != setting && setting.Equals( this.setting ) ) return; // // Store the new setting details. // System.Diagnostics.Debug.Write( "INFO CONF Refreshing cached setting '" + key + "' (thread=" + Thread.CurrentThread.GetHashCode().ToString() + ", user='" + WindowsIdentity.GetCurrent().Name + "')\r\n" ); readWriteLock.AcquireWriterLock( Timeout.Infinite ); try { this.exists = exists; this.setting = setting; } finally { readWriteLock.ReleaseWriterLock(); if( null != ConfigChange ) ConfigChange(); } } /// **************************************************************** /// public Exists /// ---------------------------------------------------------------- /// /// /// **************************************************************** /// public bool Exists { get { // // Don't call CheckSetting() here because we don't want to throw an exception if the setting // does not exist. // readWriteLock.AcquireReaderLock( Constants.ReadLockTimeout ); try { // // Make sure that our configuration is still valid. // CheckIsConfigValid(); return exists; } finally { readWriteLock.ReleaseReaderLock(); } } } /// **************************************************************** /// public HasDefaultValue /// ---------------------------------------------------------------- /// /// /// **************************************************************** /// public bool HasDefaultValue { get { return defaultSpecified; } } /// **************************************************************** /// public DefaultValue /// ---------------------------------------------------------------- /// /// /// **************************************************************** /// public object DefaultValue { get { return defaultValue; } } /// **************************************************************** /// public GetString /// ---------------------------------------------------------------- /// /// /// **************************************************************** /// public string GetString() { // // 730294 - Centralize checking of setting state and validity. // CheckSetting(); // // Return the setting as a string. // readWriteLock.AcquireReaderLock( Constants.ReadLockTimeout ); try { if( exists ) return Convert.ToString( setting ); else return Convert.ToString( defaultValue ); } finally { readWriteLock.ReleaseReaderLock(); } } /// **************************************************************** /// public GetInt /// ---------------------------------------------------------------- /// /// /// **************************************************************** /// public int GetInt() { // // 730294 - Centralize checking of setting state and validity. // CheckSetting(); // // Return the setting as an integer. // readWriteLock.AcquireReaderLock( Constants.ReadLockTimeout ); try { if( exists ) return Convert.ToInt32( setting ); else return Convert.ToInt32( defaultValue ); } finally { readWriteLock.ReleaseReaderLock(); } } /// **************************************************************** /// public GetObject /// ---------------------------------------------------------------- /// /// /// **************************************************************** /// public object GetObject() { // // 730294 - Centralize checking of setting state and validity. // CheckSetting(); // // Return the setting as an object. // readWriteLock.AcquireReaderLock( Constants.ReadLockTimeout ); try { if( exists ) return setting; else return defaultValue; } finally { readWriteLock.ReleaseReaderLock(); } } // // 730294 - Keep the state of a CachedSetting in sync with the Config by just using the Config.IsValid property instead // of keeping a valid member. // private void CheckSetting() { // // First, make sure that we still exist. // if( !exists && !defaultSpecified ) { throw new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_CONFIG_SETTING_MISSING", key ); } // // Make sure that our configuration is still valid. // CheckIsConfigValid(); } private void CheckIsConfigValid() { if( false == Config.IsValid ) { throw new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_SERVER_CONFIGURATION_ERROR", Config.LastError.Message ); } } public void AcquireReaderLock( int millisecondsTimeout ) { readWriteLock.AcquireReaderLock( millisecondsTimeout ); } public void AcquireReaderLock( TimeSpan timeout ) { readWriteLock.AcquireReaderLock( timeout ); } public void ReleaseReaderLock() { readWriteLock.ReleaseReaderLock(); } } }