/// <summary> /// Initializes the class. This includes loading application settings from the configuration file. The application name should be scoped within the system. /// For non web applications, this method must be called directly from the main executable assembly and not from a supporting library. /// /// To debug this method, create a folder called C:\AnyoneFullControl and give Everyone full control. A file will appear in that folder explaining how far /// it got in init. /// </summary> /// <param name="appName"></param> /// <param name="isClientSideProgram"></param> /// <param name="systemLogic"></param> /// <param name="mainDataAccessStateGetter">A method that returns the current main data-access state whenever it is requested, including during this /// AppTools.Init call. Do not allow multiple threads to use the same state at the same time. If you pass null, the data-access subsystem will not be /// available in the application.</param> public static void Init(string appName, bool isClientSideProgram, SystemLogic systemLogic, Func <DataAccessState> mainDataAccessStateGetter = null) { var initializationLog = "Starting init"; try { if (initialized) { throw new ApplicationException("This class can only be initialized once."); } if (systemLogic == null) { throw new ApplicationException("The system must have a global logic class and you must pass an instance of it to AppTools.Init."); } // Initialize ConfigurationStatics, including the general provider, before the exception handling block below because it's reasonable for the exception // handling to depend on this. ConfigurationStatics.Init(systemLogic.GetType(), appName, isClientSideProgram, ref initializationLog); // Setting the initialized flag to true must be done before executing the secondary init block below so that exception handling works. initialized = true; initializationLog += Environment.NewLine + "Succeeded in primary init."; } catch (Exception e) { initializationLog += Environment.NewLine + e; StandardLibraryMethods.EmergencyLog("Initialization log", initializationLog); throw; } try { var asposeLicense = ConfigurationStatics.SystemGeneralProvider.AsposeLicenseName; if (asposeLicense.Any()) { new Aspose.Pdf.License().SetLicense(asposeLicense); new Aspose.Words.License().SetLicense(asposeLicense); } // This initialization could be performed using reflection. There is no need for AppTools to have a dependency on these classes. AppMemoryCache.Init(); BlobFileOps.Init(); DataAccessStatics.Init(); DataAccessState.Init(mainDataAccessStateGetter); EncryptionOps.Init(); HtmlBlockStatics.Init(); InstallationSupportUtility.ConfigurationLogic.Init1(); UserManagementStatics.Init(); systemLogic.InitSystem(); } catch (Exception e) { secondaryInitFailed = true; // Suppress all exceptions since they would prevent apps from knowing that primary initialization succeeded. EWF apps need to know this in order to // automatically restart themselves. Other apps could find this knowledge useful as well. try { EmailAndLogError("An exception occurred during application initialization:", e); } catch {} } }
internal AppRequestState( string url ) { beginTime = DateTime.Now; MiniProfiler.Start(); this.url = url; dataAccessState = new DataAccessState( databaseConnectionInitializer: initDatabaseConnection ); dataAccessState.ResetCache(); TransferRequestPath = ""; // We cache the browser capabilities so we can determine the actual browser making the request even after modifying the capabilities, which we do later in // the life cycle from EwfPage. Browser = HttpContext.Current.Request.Browser; }
internal AppRequestState(string url) { beginInstant = SystemClock.Instance.GetCurrentInstant(); MiniProfiler.Start(); this.url = url; dataAccessState = new DataAccessState(databaseConnectionInitializer: initDatabaseConnection); dataAccessState.ResetCache(); TransferRequestPath = ""; // We cache the browser capabilities so we can determine the actual browser making the request even after modifying the capabilities, which we do later in // the life cycle from EwfPage. Browser = HttpContext.Current.Request.Browser; }
/// <summary> /// Initializes the system. This includes loading application settings from the configuration file. The application name should be scoped within the system. /// For non web applications, this method must be called directly from the main executable assembly and not from a supporting library. /// /// To debug this method, create a folder called C:\AnyoneFullControl and give Everyone full control. A file will appear in that folder explaining how far /// it got in init. /// </summary> /// <param name="globalInitializer">The system's global initializer. Do not pass null.</param> /// <param name="appName"></param> /// <param name="isClientSideApp"></param> /// <param name="assemblyFolderPath">Pass a nonempty string to override the assembly folder path, which is used to locate the installation folder. Use with /// caution.</param> /// <param name="mainDataAccessStateGetter">A method that returns the current main data-access state whenever it is requested, including during this /// InitStatics call. Do not allow multiple threads to use the same state at the same time. If you pass null, the data-access subsystem will not be /// available in the application.</param> /// <param name="useLongDatabaseTimeouts">Pass true if the application is a background process that can tolerate slow database access.</param> public static void InitStatics( SystemInitializer globalInitializer, string appName, bool isClientSideApp, string assemblyFolderPath = "", Func <DataAccessState> mainDataAccessStateGetter = null, bool useLongDatabaseTimeouts = false) { var initializationLog = "Starting init"; try { if (initialized) { throw new ApplicationException("This class can only be initialized once."); } if (globalInitializer == null) { throw new ApplicationException("The system must have a global initializer."); } // Initialize these before the exception handling block below because it's reasonable for the exception handling to depend on them. ConfigurationStatics.Init(assemblyFolderPath, globalInitializer.GetType(), appName, isClientSideApp, ref initializationLog); EmailStatics.Init(); TelemetryStatics.Init(); // Setting the initialized flag to true must be done before executing the secondary init block below so that exception handling works. initialized = true; initializationLog += Environment.NewLine + "Succeeded in primary init."; } catch (Exception e) { initializationLog += Environment.NewLine + e; EwlStatics.EmergencyLog("Initialization log", initializationLog); throw; } try { CultureInfo.DefaultThreadCurrentCulture = Cultures.EnglishUnitedStates; var asposePdfLicensePath = EwlStatics.CombinePaths(ConfigurationStatics.InstallationConfiguration.AsposeLicenseFolderPath, "Aspose.PDF.lic"); if (File.Exists(asposePdfLicensePath)) { new Aspose.Pdf.License().SetLicense(asposePdfLicensePath); } var asposeWordsLicensePath = EwlStatics.CombinePaths(ConfigurationStatics.InstallationConfiguration.AsposeLicenseFolderPath, "Aspose.Words.lic"); if (File.Exists(asposeWordsLicensePath)) { new Aspose.Words.License().SetLicense(asposeWordsLicensePath); } AppMemoryCache.Init(); BlobStorageStatics.Init(); DataAccessStatics.Init(); DataAccessState.Init(mainDataAccessStateGetter, useLongDatabaseTimeouts); EncryptionOps.Init(); HtmlBlockStatics.Init(); UserManagementStatics.Init(); GlobalInitializationOps.globalInitializer = globalInitializer; globalInitializer.InitStatics(); } catch (Exception e) { secondaryInitFailed = true; // Suppress all exceptions since they would prevent apps from knowing that primary initialization succeeded. EWF apps need to know this in order to // automatically restart themselves. Other apps could find this knowledge useful as well. try { TelemetryStatics.ReportError("An exception occurred during application initialization:", e); } catch {} } }
/// <summary> /// It's important to call this from EwfPage instead of EwfApp because requests for some pages, with their associated images, CSS files, etc., can easily /// cause 20-30 server requests, and we only want to update the time stamp once for all of these. /// </summary> private void updateLastPageRequestTimeForUser( User user ) { // Only update the request time if it's been more than a minute since we did it last. This can dramatically reduce concurrency issues caused by people // rapidly assigning tasks to one another in the System Manager or similar situations. if( ( DateTime.Now - user.LastRequestDateTime ) < TimeSpan.FromMinutes( 1 ) ) return; // Now we want to do a timestamp-based concurrency check so we don't update the last login date if we know another transaction already did. // It is not perfect, but it reduces errors caused by one user doing a long-running request and then doing smaller requests // in another browser window while the first one is still running. // We have to query in a separate transaction because otherwise snapshot isolation will result in us always getting the original LastRequestDatetime, even if // another transaction has modified its value during this transaction. var newlyQueriedUser = new DataAccessState().ExecuteWithThis( () => { Func<User> userGetter = () => UserManagementStatics.GetUser( user.UserId, false ); return ConfigurationStatics.DatabaseExists ? DataAccessState.Current.PrimaryDatabaseConnection.ExecuteWithConnectionOpen( userGetter ) : userGetter(); } ); if( newlyQueriedUser == null || newlyQueriedUser.LastRequestDateTime > user.LastRequestDateTime ) return; Action userUpdater = () => { var externalAuthProvider = UserManagementStatics.SystemProvider as ExternalAuthUserManagementProvider; if( FormsAuthStatics.FormsAuthEnabled ) { var formsAuthCapableUser = (FormsAuthCapableUser)user; FormsAuthStatics.SystemProvider.InsertOrUpdateUser( user.UserId, user.Email, user.Role.RoleId, DateTime.Now, formsAuthCapableUser.Salt, formsAuthCapableUser.SaltedPassword, formsAuthCapableUser.MustChangePassword ); } else if( externalAuthProvider != null ) externalAuthProvider.InsertOrUpdateUser( user.UserId, user.Email, user.Role.RoleId, DateTime.Now ); }; if( ConfigurationStatics.DatabaseExists ) { DataAccessState.Current.PrimaryDatabaseConnection.ExecuteInTransaction( () => { try { userUpdater(); } catch( DbConcurrencyException ) { // Since this method is called on every page request, concurrency errors are common. They are caused when an authenticated user makes one request // and then makes another before ASP.NET has finished processing the first. Since we are only updating the last request date and time, we don't // need to get an error email if the update fails. throw new DoNotCommitException(); } } ); } else userUpdater(); }