瀏覽代碼

keep localdb open during restart; restart performance improvements

Jason Rivard 5 年之前
父節點
當前提交
ff7486bf98
共有 29 個文件被更改,包括 202 次插入118 次删除
  1. 54 0
      server/src/main/java/password/pwm/AppAttribute.java
  2. 1 0
      server/src/main/java/password/pwm/AppProperty.java
  3. 5 2
      server/src/main/java/password/pwm/PwmAboutProperty.java
  4. 30 42
      server/src/main/java/password/pwm/PwmApplication.java
  5. 1 1
      server/src/main/java/password/pwm/config/function/UserMatchViewerFunction.java
  6. 2 1
      server/src/main/java/password/pwm/config/stored/ConfigurationReader.java
  7. 1 1
      server/src/main/java/password/pwm/health/DatabaseStatusChecker.java
  8. 1 1
      server/src/main/java/password/pwm/health/LDAPHealthChecker.java
  9. 15 4
      server/src/main/java/password/pwm/http/ContextManager.java
  10. 1 1
      server/src/main/java/password/pwm/http/servlet/configguide/ConfigGuideServlet.java
  11. 3 2
      server/src/main/java/password/pwm/http/servlet/configmanager/ConfigManagerLoginServlet.java
  12. 3 2
      server/src/main/java/password/pwm/ldap/LdapConnectionService.java
  13. 3 2
      server/src/main/java/password/pwm/svc/report/ReportService.java
  14. 3 2
      server/src/main/java/password/pwm/svc/telemetry/TelemetryService.java
  15. 3 2
      server/src/main/java/password/pwm/svc/wordlist/LocalDBWordlistBucket.java
  16. 4 4
      server/src/main/java/password/pwm/svc/wordlist/WordlistConfiguration.java
  17. 1 1
      server/src/main/java/password/pwm/util/OnejarHelper.java
  18. 1 1
      server/src/main/java/password/pwm/util/cli/MainClass.java
  19. 2 1
      server/src/main/java/password/pwm/util/cli/commands/ResetInstanceIDCommand.java
  20. 2 2
      server/src/main/java/password/pwm/util/java/TimeDuration.java
  21. 6 26
      server/src/main/java/password/pwm/util/localdb/LocalDBAdaptor.java
  22. 7 7
      server/src/main/java/password/pwm/util/localdb/LocalDBFactory.java
  23. 32 0
      server/src/main/java/password/pwm/util/localdb/LocalDBStatistics.java
  24. 5 5
      server/src/main/java/password/pwm/util/localdb/LocalDBUtility.java
  25. 8 3
      server/src/main/java/password/pwm/util/localdb/WorkQueueProcessor.java
  26. 3 2
      server/src/main/java/password/pwm/util/logging/LocalDBLogger.java
  27. 3 2
      server/src/main/java/password/pwm/util/secure/HttpsServerCertificateManager.java
  28. 1 0
      server/src/main/resources/password/pwm/AppProperty.properties
  29. 1 1
      server/src/test/java/password/pwm/util/localdb/TestHelper.java

+ 54 - 0
server/src/main/java/password/pwm/AppAttribute.java

@@ -0,0 +1,54 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2019 The PWM Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package password.pwm;
+
+public enum AppAttribute
+{
+    INSTANCE_ID( "context_instanceID" ),
+    INSTALL_DATE( "DB_KEY_INSTALL_DATE" ),
+    CONFIG_HASH( "configurationSettingHash" ),
+    LAST_LDAP_ERROR( "lastLdapError" ),
+    // TOKEN_COUNTER( "tokenCounter" ), deprecated
+    REPORT_STATUS( "reporting.status" ),
+    // REPORT_CLEAN_FLAG("reporting.cleanFlag"), deprecated
+    SMS_ITEM_COUNTER( "smsQueue.itemCount" ),
+    EMAIL_ITEM_COUNTER( "itemQueue.itemCount" ),
+    LOCALDB_IMPORT_STATUS( "localDB.import.status" ),
+    WORDLIST_METADATA( "wordlist.metadata" ),
+    SEEDLIST_METADATA( "seedlist.metadata" ),
+    HTTPS_SELF_CERT( "https.selfCert" ),
+    CONFIG_LOGIN_HISTORY( "config.loginHistory" ),
+    LOCALDB_LOGGER_STORAGE_FORMAT( "localdb.logger.storage.format" ),
+
+    TELEMETRY_LAST_PUBLISH_TIMESTAMP( "telemetry.lastPublish.timestamp" );
+
+    private final String key;
+
+    AppAttribute( final String key )
+    {
+        this.key = key;
+    }
+
+    public String getKey( )
+    {
+        return key;
+    }
+}

+ 1 - 0
server/src/main/java/password/pwm/AppProperty.java

@@ -176,6 +176,7 @@ public enum AppProperty
     LOCALDB_LOGWRITER_BUFFER_SIZE                   ( "localdb.logWriter.bufferSize" ),
     LOCALDB_LOGWRITER_MAX_BUFFER_WAIT_MS            ( "localdb.logWriter.maxBufferWaitMs" ),
     LOCALDB_LOGWRITER_MAX_TRIM_SIZE                 ( "localdb.logWriter.maxTrimSize" ),
+    LOCALDB_RELOAD_WHEN_APP_RESTARTED               ( "localdb.reloadWhenAppRestarted" ),
     MACRO_RANDOM_CHAR_MAX_LENGTH                    ( "macro.randomChar.maxLength" ),
     MACRO_LDAP_ATTR_CHAR_MAX_LENGTH                 ( "macro.ldapAttr.maxLength" ),
 

+ 5 - 2
server/src/main/java/password/pwm/PwmAboutProperty.java

@@ -142,8 +142,11 @@ public enum PwmAboutProperty
                 }
                 catch ( final Throwable t )
                 {
-                    aboutMap.put( pwmAboutProperty.name(), LocaleHelper.getLocalizedMessage( null, Display.Value_NotApplicable, null ) );
-                    LOGGER.trace( () -> "error generating about value for '" + pwmAboutProperty.name() + "', error: " + t.getMessage() );
+                    if ( !( t instanceof NullPointerException ) )
+                    {
+                        aboutMap.put( pwmAboutProperty.name(), LocaleHelper.getLocalizedMessage( null, Display.Value_NotApplicable, null ) );
+                        LOGGER.trace( () -> "error generating about value for '" + pwmAboutProperty.name() + "', error: " + t.getMessage() );
+                    }
                 }
             }
         }

+ 30 - 42
server/src/main/java/password/pwm/PwmApplication.java

@@ -118,40 +118,6 @@ public class PwmApplication
     private static final PwmLogger LOGGER = PwmLogger.forClass( PwmApplication.class );
     private static final String DEFAULT_INSTANCE_ID = "-1";
 
-    public enum AppAttribute
-    {
-        INSTANCE_ID( "context_instanceID" ),
-        INSTALL_DATE( "DB_KEY_INSTALL_DATE" ),
-        CONFIG_HASH( "configurationSettingHash" ),
-        LAST_LDAP_ERROR( "lastLdapError" ),
-        // TOKEN_COUNTER( "tokenCounter" ), deprecated
-        REPORT_STATUS( "reporting.status" ),
-        // REPORT_CLEAN_FLAG("reporting.cleanFlag"), deprecated
-        SMS_ITEM_COUNTER( "smsQueue.itemCount" ),
-        EMAIL_ITEM_COUNTER( "itemQueue.itemCount" ),
-        LOCALDB_IMPORT_STATUS( "localDB.import.status" ),
-        WORDLIST_METADATA( "wordlist.metadata" ),
-        SEEDLIST_METADATA( "seedlist.metadata" ),
-        HTTPS_SELF_CERT( "https.selfCert" ),
-        CONFIG_LOGIN_HISTORY( "config.loginHistory" ),
-        LOCALDB_LOGGER_STORAGE_FORMAT( "localdb.logger.storage.format" ),
-
-        TELEMETRY_LAST_PUBLISH_TIMESTAMP( "telemetry.lastPublish.timestamp" );
-
-        private final String key;
-
-        AppAttribute( final String key )
-        {
-            this.key = key;
-        }
-
-        public String getKey( )
-        {
-            return key;
-        }
-    }
-
-
     private String instanceID = DEFAULT_INSTANCE_ID;
     private String runtimeNonce = PwmRandom.getInstance().randomUUID().toString();
 
@@ -169,11 +135,12 @@ public class PwmApplication
 
     private PwmScheduler pwmScheduler;
 
-    public PwmApplication( final PwmEnvironment pwmEnvironment )
+    private PwmApplication( final PwmEnvironment pwmEnvironment, final LocalDB localDB )
             throws PwmUnrecoverableException
     {
-        pwmEnvironment.verifyIfApplicationPathIsSetProperly();
+        this.localDB = localDB;
         this.pwmEnvironment = pwmEnvironment;
+        pwmEnvironment.verifyIfApplicationPathIsSetProperly();
 
         try
         {
@@ -186,6 +153,16 @@ public class PwmApplication
         }
     }
 
+    public static PwmApplication createPwmApplication( final PwmEnvironment pwmEnvironment ) throws PwmUnrecoverableException
+    {
+        return new PwmApplication( pwmEnvironment, null );
+    }
+
+    public static PwmApplication createPwmApplication( final PwmEnvironment pwmEnvironment, final LocalDB localDB ) throws PwmUnrecoverableException
+    {
+        return new PwmApplication( pwmEnvironment, localDB );
+    }
+
     private void initialize( )
             throws PwmUnrecoverableException
     {
@@ -265,7 +242,10 @@ public class PwmApplication
             }
             else
             {
-                this.localDB = Initializer.initializeLocalDB( this );
+                if ( localDB == null )
+                {
+                    this.localDB = Initializer.initializeLocalDB( this, pwmEnvironment );
+                }
             }
         }
 
@@ -806,6 +786,11 @@ public class PwmApplication
     }
 
     public void shutdown( )
+    {
+        shutdown( true );
+    }
+
+    public void shutdown( final boolean leaveLocalDbOpen )
     {
         pwmScheduler.shutdown();
 
@@ -846,7 +831,11 @@ public class PwmApplication
             localDBLogger = null;
         }
 
-        if ( localDB != null )
+        if ( leaveLocalDbOpen )
+        {
+            LOGGER.trace( () -> "skipping close of LocalDB (restart request)" );
+        }
+        else if ( localDB != null )
         {
             try
             {
@@ -882,11 +871,10 @@ public class PwmApplication
 
     private static class Initializer
     {
-
-        public static LocalDB initializeLocalDB( final PwmApplication pwmApplication ) throws PwmUnrecoverableException
+        public static LocalDB initializeLocalDB( final PwmApplication pwmApplication, final PwmEnvironment pwmEnvironment ) throws PwmUnrecoverableException
         {
             final File databaseDirectory;
-            // see if META-INF isn't already there, then use WEB-INF.
+
             try
             {
                 final String localDBLocationSetting = pwmApplication.getConfig().readAppProperty( AppProperty.LOCALDB_LOCATION );
@@ -905,7 +893,7 @@ public class PwmApplication
             try
             {
                 final boolean readOnly = pwmApplication.getApplicationMode() == PwmApplicationMode.READ_ONLY;
-                return LocalDBFactory.getInstance( databaseDirectory, readOnly, pwmApplication, pwmApplication.getConfig() );
+                return LocalDBFactory.getInstance( databaseDirectory, readOnly, pwmEnvironment, pwmApplication.getConfig() );
             }
             catch ( final Exception e )
             {

+ 1 - 1
server/src/main/java/password/pwm/config/function/UserMatchViewerFunction.java

@@ -96,7 +96,7 @@ public class UserMatchViewerFunction implements SettingUIFunction
             throws Exception
     {
         final Configuration config = new Configuration( storedConfiguration );
-        final PwmApplication tempApplication = new PwmApplication( pwmApplication.getPwmEnvironment().makeRuntimeInstance( config ) );
+        final PwmApplication tempApplication = PwmApplication.createPwmApplication( pwmApplication.getPwmEnvironment().makeRuntimeInstance( config ) );
         final List<UserPermission> permissions = ( List<UserPermission> ) storedConfiguration.readSetting( setting, profile ).toNativeObject();
 
         for ( final UserPermission userPermission : permissions )

+ 2 - 1
server/src/main/java/password/pwm/config/stored/ConfigurationReader.java

@@ -20,6 +20,7 @@
 
 package password.pwm.config.stored;
 
+import password.pwm.AppAttribute;
 import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplicationMode;
@@ -334,7 +335,7 @@ public class ConfigurationReader
         if ( pwmApplication != null )
         {
             final String actualChecksum = storedConfiguration.valueHash();
-            pwmApplication.writeAppAttribute( PwmApplication.AppAttribute.CONFIG_HASH, actualChecksum );
+            pwmApplication.writeAppAttribute( AppAttribute.CONFIG_HASH, actualChecksum );
         }
 
         LOGGER.trace( () -> "renaming file " + tempWriteFile.getAbsolutePath() + " to " + configFile.getAbsolutePath() );

+ 1 - 1
server/src/main/java/password/pwm/health/DatabaseStatusChecker.java

@@ -57,7 +57,7 @@ public class DatabaseStatusChecker implements HealthChecker
         try
         {
             final PwmEnvironment runtimeEnvironment = pwmApplication.getPwmEnvironment().makeRuntimeInstance( config );
-            runtimeInstance = new PwmApplication( runtimeEnvironment );
+            runtimeInstance = PwmApplication.createPwmApplication( runtimeEnvironment );
             final DatabaseAccessor accessor = runtimeInstance.getDatabaseService().getAccessor();
             accessor.get( DatabaseTable.PWM_META, "test" );
             return runtimeInstance.getDatabaseService().healthCheck();

+ 1 - 1
server/src/main/java/password/pwm/health/LDAPHealthChecker.java

@@ -1156,7 +1156,7 @@ public class LDAPHealthChecker implements HealthChecker
     )
             throws PwmUnrecoverableException
     {
-        final PwmApplication tempApplication = new PwmApplication( pwmApplication.getPwmEnvironment().makeRuntimeInstance( config ) );
+        final PwmApplication tempApplication = PwmApplication.createPwmApplication( pwmApplication.getPwmEnvironment().makeRuntimeInstance( config ) );
         final LDAPHealthChecker ldapHealthChecker = new LDAPHealthChecker();
         final List<HealthRecord> profileRecords = new ArrayList<>();
 

+ 15 - 4
server/src/main/java/password/pwm/http/ContextManager.java

@@ -45,6 +45,7 @@ import password.pwm.util.PwmScheduler;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
+import password.pwm.util.localdb.LocalDB;
 import password.pwm.util.logging.PwmLogManager;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.X509Utils;
@@ -196,6 +197,11 @@ public class ContextManager implements Serializable
     }
 
     public void initialize( )
+    {
+        this.initialize( null );
+    }
+
+    private void initialize( final LocalDB preExistingLocalDB )
     {
         final Instant startTime = Instant.now();
 
@@ -274,7 +280,7 @@ public class ContextManager implements Serializable
                     .setFlags( applicationFlags )
                     .setParams( applicationParams )
                     .createPwmEnvironment();
-            pwmApplication = new PwmApplication( pwmEnvironment );
+            pwmApplication = PwmApplication.createPwmApplication( pwmEnvironment, preExistingLocalDB );
         }
         catch ( final Exception e )
         {
@@ -393,7 +399,7 @@ public class ContextManager implements Serializable
 
     public void requestPwmApplicationRestart( )
     {
-        LOGGER.debug( () -> "immediate restart requested" );
+        LOGGER.debug( SESSION_LABEL, () -> "immediate restart requested" );
         taskMaster.schedule( new RestartFlagWatcher(), 0, TimeUnit.MILLISECONDS );
     }
 
@@ -478,6 +484,7 @@ public class ContextManager implements Serializable
 
         private void doRestart( )
         {
+            final boolean reloadLocalDB = Boolean.parseBoolean( pwmApplication.getConfig().readAppProperty( AppProperty.LOCALDB_RELOAD_WHEN_APP_RESTARTED ) );
             final Instant startTime = Instant.now();
 
             if ( restartInProgressFlag.get() )
@@ -494,6 +501,10 @@ public class ContextManager implements Serializable
             }
 
             final PwmApplication oldPwmApplication = pwmApplication;
+            final LocalDB oldLocalDB = reloadLocalDB
+                    ? null
+                    : pwmApplication.getLocalDB();
+
             pwmApplication = null;
 
             try
@@ -515,7 +526,7 @@ public class ContextManager implements Serializable
                         // prevent restart watcher from detecting in-progress restart in a loop
                         taskMaster.shutdown();
 
-                        oldPwmApplication.shutdown();
+                        oldPwmApplication.shutdown( oldLocalDB != null );
                     }
                     catch ( final Exception e )
                     {
@@ -535,7 +546,7 @@ public class ContextManager implements Serializable
                             + ") now starting new application instance ("
                             + timeDuration.asCompactString() + ")" );
                 }
-                initialize();
+                initialize( oldLocalDB );
 
                 {
                     final TimeDuration timeDuration = TimeDuration.fromCurrent( startTime );

+ 1 - 1
server/src/main/java/password/pwm/http/servlet/configguide/ConfigGuideServlet.java

@@ -235,7 +235,7 @@ public class ConfigGuideServlet extends ControlledPwmServlet
 
         final StoredConfiguration storedConfiguration = ConfigGuideForm.generateStoredConfig( configGuideBean );
         final Configuration tempConfiguration = new Configuration( storedConfiguration );
-        final PwmApplication tempApplication = new PwmApplication( pwmRequest.getPwmApplication()
+        final PwmApplication tempApplication = PwmApplication.createPwmApplication( pwmRequest.getPwmApplication()
                 .getPwmEnvironment()
                 .makeRuntimeInstance( tempConfiguration ) );
 

+ 3 - 2
server/src/main/java/password/pwm/http/servlet/configmanager/ConfigManagerLoginServlet.java

@@ -23,6 +23,7 @@ package password.pwm.http.servlet.configmanager;
 import com.google.gson.annotations.SerializedName;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import lombok.Value;
+import password.pwm.AppAttribute;
 import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplicationMode;
@@ -195,7 +196,7 @@ public class ConfigManagerLoginServlet extends AbstractPwmServlet
 
     private static ConfigLoginHistory readConfigLoginHistory( final PwmRequest pwmRequest )
     {
-        final ConfigLoginHistory configLoginHistory = pwmRequest.getPwmApplication().readAppAttribute( PwmApplication.AppAttribute.CONFIG_LOGIN_HISTORY, ConfigLoginHistory.class );
+        final ConfigLoginHistory configLoginHistory = pwmRequest.getPwmApplication().readAppAttribute( AppAttribute.CONFIG_LOGIN_HISTORY, ConfigLoginHistory.class );
         return configLoginHistory == null
                 ? new ConfigLoginHistory()
                 : configLoginHistory;
@@ -211,7 +212,7 @@ public class ConfigManagerLoginServlet extends AbstractPwmServlet
         );
         final int maxEvents = Integer.parseInt( pwmRequest.getPwmApplication().getConfig().readAppProperty( AppProperty.CONFIG_HISTORY_MAX_ITEMS ) );
         configLoginHistory.addEvent( event, maxEvents, successful );
-        pwmRequest.getPwmApplication().writeAppAttribute( PwmApplication.AppAttribute.CONFIG_LOGIN_HISTORY, configLoginHistory );
+        pwmRequest.getPwmApplication().writeAppAttribute( AppAttribute.CONFIG_LOGIN_HISTORY, configLoginHistory );
     }
 
     @Value

+ 3 - 2
server/src/main/java/password/pwm/ldap/LdapConnectionService.java

@@ -28,6 +28,7 @@ import com.novell.ldapchai.provider.ProviderStatistics;
 import lombok.Builder;
 import lombok.Data;
 import lombok.Value;
+import password.pwm.AppAttribute;
 import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
 import password.pwm.config.option.DataStorageMethod;
@@ -294,7 +295,7 @@ public class LdapConnectionService implements PwmService
     {
         lastLdapErrors.put( ldapProfile.getIdentifier(), errorInformation );
         final String jsonString = JsonUtil.serializeMap( lastLdapErrors );
-        pwmApplication.writeAppAttribute( PwmApplication.AppAttribute.LAST_LDAP_ERROR, jsonString );
+        pwmApplication.writeAppAttribute( AppAttribute.LAST_LDAP_ERROR, jsonString );
     }
 
     public Map<String, ErrorInformation> getLastLdapFailure( )
@@ -317,7 +318,7 @@ public class LdapConnectionService implements PwmService
         String lastLdapFailureStr = null;
         try
         {
-            lastLdapFailureStr = pwmApplication.readAppAttribute( PwmApplication.AppAttribute.LAST_LDAP_ERROR, String.class );
+            lastLdapFailureStr = pwmApplication.readAppAttribute( AppAttribute.LAST_LDAP_ERROR, String.class );
             if ( lastLdapFailureStr != null && lastLdapFailureStr.length() > 0 )
             {
                 final Map<String, ErrorInformation> fromJson = JsonUtil.deserialize( lastLdapFailureStr, new TypeToken<Map<String, ErrorInformation>>()

+ 3 - 2
server/src/main/java/password/pwm/svc/report/ReportService.java

@@ -20,6 +20,7 @@
 
 package password.pwm.svc.report;
 
+import password.pwm.AppAttribute;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplicationMode;
 import password.pwm.bean.SessionLabel;
@@ -168,7 +169,7 @@ public class ReportService implements PwmService
     {
         try
         {
-            pwmApplication.writeAppAttribute( PwmApplication.AppAttribute.REPORT_STATUS, reportStatus.get() );
+            pwmApplication.writeAppAttribute( AppAttribute.REPORT_STATUS, reportStatus.get() );
         }
         catch ( final Exception e )
         {
@@ -658,7 +659,7 @@ public class ReportService implements PwmService
         {
             try
             {
-                final ReportStatusInfo localReportStatus = pwmApplication.readAppAttribute( PwmApplication.AppAttribute.REPORT_STATUS, ReportStatusInfo.class );
+                final ReportStatusInfo localReportStatus = pwmApplication.readAppAttribute( AppAttribute.REPORT_STATUS, ReportStatusInfo.class );
                 reportStatus.set( localReportStatus );
             }
             catch ( final Exception e )

+ 3 - 2
server/src/main/java/password/pwm/svc/telemetry/TelemetryService.java

@@ -23,6 +23,7 @@ package password.pwm.svc.telemetry;
 import com.novell.ldapchai.provider.DirectoryVendor;
 import lombok.Builder;
 import lombok.Getter;
+import password.pwm.AppAttribute;
 import password.pwm.AppProperty;
 import password.pwm.PwmAboutProperty;
 import password.pwm.PwmApplication;
@@ -135,7 +136,7 @@ public class TelemetryService implements PwmService
         }
 
         {
-            final Instant storedLastPublishTimestamp = pwmApplication.readAppAttribute( PwmApplication.AppAttribute.TELEMETRY_LAST_PUBLISH_TIMESTAMP, Instant.class );
+            final Instant storedLastPublishTimestamp = pwmApplication.readAppAttribute( AppAttribute.TELEMETRY_LAST_PUBLISH_TIMESTAMP, Instant.class );
             lastPublishTime = storedLastPublishTimestamp != null
                     ? storedLastPublishTimestamp
                     : pwmApplication.getInstallTime();
@@ -206,7 +207,7 @@ public class TelemetryService implements PwmService
         }
 
         lastPublishTime = Instant.now();
-        pwmApplication.writeAppAttribute( PwmApplication.AppAttribute.TELEMETRY_LAST_PUBLISH_TIMESTAMP, lastPublishTime );
+        pwmApplication.writeAppAttribute( AppAttribute.TELEMETRY_LAST_PUBLISH_TIMESTAMP, lastPublishTime );
         scheduleNextJob();
     }
 

+ 3 - 2
server/src/main/java/password/pwm/svc/wordlist/LocalDBWordlistBucket.java

@@ -20,6 +20,7 @@
 
 package password.pwm.svc.wordlist;
 
+import password.pwm.AppAttribute;
 import password.pwm.PwmApplication;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
@@ -117,7 +118,7 @@ class LocalDBWordlistBucket extends AbstractWordlistBucket implements WordlistBu
     @Override
     public WordlistStatus readWordlistStatus()
     {
-        final PwmApplication.AppAttribute appAttribute = wordlistConfiguration.getMetaDataAppAttribute();
+        final AppAttribute appAttribute = wordlistConfiguration.getMetaDataAppAttribute();
         final WordlistStatus storedValue = pwmApplication.readAppAttribute( appAttribute, WordlistStatus.class );
         if ( storedValue != null )
         {
@@ -129,7 +130,7 @@ class LocalDBWordlistBucket extends AbstractWordlistBucket implements WordlistBu
     @Override
     public void writeWordlistStatus( final WordlistStatus wordlistStatus )
     {
-        final PwmApplication.AppAttribute appAttribute = wordlistConfiguration.getMetaDataAppAttribute();
+        final AppAttribute appAttribute = wordlistConfiguration.getMetaDataAppAttribute();
         pwmApplication.writeAppAttribute( appAttribute, wordlistStatus );
     }
 

+ 4 - 4
server/src/main/java/password/pwm/svc/wordlist/WordlistConfiguration.java

@@ -24,8 +24,8 @@ import lombok.AccessLevel;
 import lombok.Builder;
 import lombok.Getter;
 import lombok.Value;
+import password.pwm.AppAttribute;
 import password.pwm.AppProperty;
-import password.pwm.PwmApplication;
 import password.pwm.config.Configuration;
 import password.pwm.config.PwmSetting;
 import password.pwm.error.PwmUnrecoverableException;
@@ -55,7 +55,7 @@ public class WordlistConfiguration implements Serializable
     private final String autoImportUrl;
     private final int minWordSize;
     private final int maxWordSize;
-    private final PwmApplication.AppAttribute metaDataAppAttribute;
+    private final AppAttribute metaDataAppAttribute;
     private final AppProperty builtInWordlistLocationProperty;
     private final LocalDB.DB db;
     private final PwmSetting wordlistFilenameSetting;
@@ -84,7 +84,7 @@ public class WordlistConfiguration implements Serializable
             {
                 return commonBuilder( configuration, type ).toBuilder()
                         .autoImportUrl( readAutoImportUrl( configuration, PwmSetting.SEEDLIST_FILENAME ) )
-                        .metaDataAppAttribute( PwmApplication.AppAttribute.SEEDLIST_METADATA )
+                        .metaDataAppAttribute( AppAttribute.SEEDLIST_METADATA )
                         .builtInWordlistLocationProperty( AppProperty.SEEDLIST_BUILTIN_PATH )
                         .db( LocalDB.DB.SEEDLIST_WORDS )
                         .wordlistFilenameSetting( PwmSetting.SEEDLIST_FILENAME )
@@ -97,7 +97,7 @@ public class WordlistConfiguration implements Serializable
                         .caseSensitive( configuration.readSettingAsBoolean( PwmSetting.WORDLIST_CASE_SENSITIVE )  )
                         .checkSize( (int) configuration.readSettingAsLong( PwmSetting.PASSWORD_WORDLIST_WORDSIZE ) )
                         .autoImportUrl( readAutoImportUrl( configuration, PwmSetting.WORDLIST_FILENAME ) )
-                        .metaDataAppAttribute( PwmApplication.AppAttribute.WORDLIST_METADATA )
+                        .metaDataAppAttribute( AppAttribute.WORDLIST_METADATA )
                         .builtInWordlistLocationProperty( AppProperty.WORDLIST_BUILTIN_PATH )
                         .db( LocalDB.DB.WORDLIST_WORDS )
                         .wordlistFilenameSetting( PwmSetting.WORDLIST_FILENAME )

+ 1 - 1
server/src/main/java/password/pwm/util/OnejarHelper.java

@@ -118,6 +118,6 @@ public class OnejarHelper
                 .setFlags( Collections.singleton( PwmEnvironment.ApplicationFlag.CommandLineInstance ) )
                 .setInternalRuntimeInstance( true )
                 .createPwmEnvironment();
-        return new PwmApplication( pwmEnvironment );
+        return PwmApplication.createPwmApplication( pwmEnvironment );
     }
 }

+ 1 - 1
server/src/main/java/password/pwm/util/cli/MainClass.java

@@ -492,7 +492,7 @@ public class MainClass
                 .setConfigurationFile( configurationFile )
                 .setFlags( applicationFlags )
                 .createPwmEnvironment();
-        final PwmApplication pwmApplication = new PwmApplication( pwmEnvironment );
+        final PwmApplication pwmApplication = PwmApplication.createPwmApplication( pwmEnvironment );
         final PwmApplicationMode runningMode = pwmApplication.getApplicationMode();
 
         if ( runningMode != mode )

+ 2 - 1
server/src/main/java/password/pwm/util/cli/commands/ResetInstanceIDCommand.java

@@ -20,6 +20,7 @@
 
 package password.pwm.util.cli.commands;
 
+import password.pwm.AppAttribute;
 import password.pwm.PwmApplication;
 import password.pwm.util.cli.CliParameters;
 
@@ -35,7 +36,7 @@ public class ResetInstanceIDCommand extends AbstractCliCommand
             return;
         }
 
-        pwmApplication.writeAppAttribute( PwmApplication.AppAttribute.INSTANCE_ID, null );
+        pwmApplication.writeAppAttribute( AppAttribute.INSTANCE_ID, null );
         out( "instanceID has been cleared" );
     }
 

+ 2 - 2
server/src/main/java/password/pwm/util/java/TimeDuration.java

@@ -462,7 +462,7 @@ public class TimeDuration implements Comparable, Serializable
             final BooleanSupplier interruptBoolean
     )
     {
-        final long interruptMs = JavaHelper.rangeCheck( 5, 1000, this.asMillis() / 100 );
+        final long interruptMs = this.asMillis() / 100;
         return pause( TimeDuration.of( interruptMs, Unit.MILLISECONDS ), interruptBoolean );
     }
 
@@ -473,7 +473,7 @@ public class TimeDuration implements Comparable, Serializable
     )
     {
         final long startTime = System.currentTimeMillis();
-        final long pauseTime = JavaHelper.rangeCheck( this.asMillis(), this.asMillis(), interruptCheckInterval.asMillis()  );
+        final long pauseTime = JavaHelper.rangeCheck( 5, 1000, interruptCheckInterval.asMillis() );
 
         while ( ( System.currentTimeMillis() - startTime ) < this.asMillis() && !interruptBoolean.getAsBoolean() )
         {

+ 6 - 26
server/src/main/java/password/pwm/util/localdb/LocalDBAdaptor.java

@@ -20,32 +20,24 @@
 
 package password.pwm.util.localdb;
 
-import password.pwm.PwmApplication;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
-import password.pwm.svc.stats.EpsStatistic;
 
 import java.io.File;
 import java.io.Serializable;
 import java.util.Collection;
 import java.util.Map;
+import java.util.Objects;
 
 public class LocalDBAdaptor implements LocalDB
 {
     private final LocalDBProvider innerDB;
+    private final LocalDBStatistics localDBStatistics = new LocalDBStatistics();
 
-    private final PwmApplication pwmApplication;
-
-    LocalDBAdaptor( final LocalDBProvider innerDB, final PwmApplication pwmApplication )
+    LocalDBAdaptor( final LocalDBProvider innerDB )
     {
-        this.pwmApplication = pwmApplication;
-        if ( innerDB == null )
-        {
-            throw new IllegalArgumentException( "innerDB can not be null" );
-        }
-
+        Objects.requireNonNull( innerDB );
         this.innerDB = innerDB;
-
     }
 
     public File getFileLocation( )
@@ -259,23 +251,11 @@ public class LocalDBAdaptor implements LocalDB
 
     private void markRead()
     {
-        if ( pwmApplication != null )
-        {
-            if ( pwmApplication.getStatisticsManager() != null )
-            {
-                pwmApplication.getStatisticsManager().updateEps( EpsStatistic.PWMDB_READS, 1 );
-            }
-        }
+       this.localDBStatistics.getReadOperations().incrementAndGet();
     }
 
     private void markWrite( final int events )
     {
-        if ( pwmApplication != null )
-        {
-            if ( pwmApplication.getStatisticsManager() != null )
-            {
-                pwmApplication.getStatisticsManager().updateEps( EpsStatistic.PWMDB_WRITES, events );
-            }
-        }
+        this.localDBStatistics.getWriteOperations().addAndGet( events );
     }
 }

+ 7 - 7
server/src/main/java/password/pwm/util/localdb/LocalDBFactory.java

@@ -21,7 +21,7 @@
 package password.pwm.util.localdb;
 
 import password.pwm.AppProperty;
-import password.pwm.PwmApplication;
+import password.pwm.PwmEnvironment;
 import password.pwm.config.Configuration;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
@@ -46,13 +46,13 @@ public class LocalDBFactory
     public static synchronized LocalDB getInstance(
             final File dbDirectory,
             final boolean readonly,
-            final PwmApplication pwmApplication,
+            final PwmEnvironment pwmEnvironment,
             final Configuration configuration
     )
             throws Exception
     {
-        final Configuration config = ( configuration == null && pwmApplication != null )
-                ? pwmApplication.getConfig()
+        final Configuration config = ( configuration == null && pwmEnvironment != null )
+                ? pwmEnvironment.getConfig()
                 : configuration;
 
         final long startTime = System.currentTimeMillis();
@@ -72,13 +72,13 @@ public class LocalDBFactory
             initParameters = StringUtil.convertStringListToNameValuePair( Arrays.asList( initStrings.split( ";;;" ) ), "=" );
         }
 
-        final Map<LocalDBProvider.Parameter, String> parameters = pwmApplication == null
+        final Map<LocalDBProvider.Parameter, String> parameters = pwmEnvironment == null
                 ? Collections.emptyMap()
-                : makeParameterMap( pwmApplication.getConfig(), readonly );
+                : makeParameterMap( pwmEnvironment.getConfig(), readonly );
         final LocalDBProvider dbProvider = createInstance( className );
         LOGGER.debug( () -> "initializing " + className + " localDBProvider instance" );
 
-        final LocalDB localDB = new LocalDBAdaptor( dbProvider, pwmApplication );
+        final LocalDB localDB = new LocalDBAdaptor( dbProvider );
 
         initInstance( dbProvider, dbDirectory, initParameters, className, parameters );
         final TimeDuration openTime = TimeDuration.of( System.currentTimeMillis() - startTime, TimeDuration.Unit.MILLISECONDS );

+ 32 - 0
server/src/main/java/password/pwm/util/localdb/LocalDBStatistics.java

@@ -0,0 +1,32 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2019 The PWM Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package password.pwm.util.localdb;
+
+import lombok.Value;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+@Value
+public class LocalDBStatistics
+{
+    private final AtomicLong readOperations = new AtomicLong( 0 );
+    private final AtomicLong writeOperations = new AtomicLong( 0 );
+}

+ 5 - 5
server/src/main/java/password/pwm/util/localdb/LocalDBUtility.java

@@ -23,7 +23,7 @@ package password.pwm.util.localdb;
 import org.apache.commons.csv.CSVPrinter;
 import org.apache.commons.csv.CSVRecord;
 import org.apache.commons.io.input.CountingInputStream;
-import password.pwm.PwmApplication;
+import password.pwm.AppAttribute;
 import password.pwm.PwmConstants;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmOperationalException;
@@ -371,7 +371,7 @@ public class LocalDBUtility
                 throws LocalDBException
         {
             LOGGER.info( () -> "preparing LocalDB for import procedure" );
-            localDB.put( LocalDB.DB.PWM_META, PwmApplication.AppAttribute.LOCALDB_IMPORT_STATUS.getKey(), IN_PROGRESS_STATUS_VALUE );
+            localDB.put( LocalDB.DB.PWM_META, AppAttribute.LOCALDB_IMPORT_STATUS.getKey(), IN_PROGRESS_STATUS_VALUE );
             for ( final LocalDB.DB loopDB : LocalDB.DB.values() )
             {
                 if ( loopDB != LocalDB.DB.PWM_META )
@@ -382,14 +382,14 @@ public class LocalDBUtility
 
             // save meta for last so flag is cleared last.
             localDB.truncate( LocalDB.DB.PWM_META );
-            localDB.put( LocalDB.DB.PWM_META, PwmApplication.AppAttribute.LOCALDB_IMPORT_STATUS.getKey(), IN_PROGRESS_STATUS_VALUE  );
+            localDB.put( LocalDB.DB.PWM_META, AppAttribute.LOCALDB_IMPORT_STATUS.getKey(), IN_PROGRESS_STATUS_VALUE  );
         }
 
         private void markImportComplete()
                 throws LocalDBException
         {
             LOGGER.info( () -> "marking LocalDB import procedure completed" );
-            localDB.remove( LocalDB.DB.PWM_META, PwmApplication.AppAttribute.LOCALDB_IMPORT_STATUS.getKey() );
+            localDB.remove( LocalDB.DB.PWM_META, AppAttribute.LOCALDB_IMPORT_STATUS.getKey() );
         }
 
         private String debugStatsString()
@@ -466,7 +466,7 @@ public class LocalDBUtility
             throws LocalDBException
     {
         return IN_PROGRESS_STATUS_VALUE.equals(
-                localDB.get( LocalDB.DB.PWM_META, PwmApplication.AppAttribute.LOCALDB_IMPORT_STATUS.getKey() ) );
+                localDB.get( LocalDB.DB.PWM_META, AppAttribute.LOCALDB_IMPORT_STATUS.getKey() ) );
     }
 
     static boolean hasBooleanParameter( final LocalDBProvider.Parameter parameter, final Map<LocalDBProvider.Parameter, String> parameters )

+ 8 - 3
server/src/main/java/password/pwm/util/localdb/WorkQueueProcessor.java

@@ -60,7 +60,7 @@ import java.util.concurrent.locks.LockSupport;
 public final class WorkQueueProcessor<W extends Serializable>
 {
     private static final TimeDuration SUBMIT_QUEUE_FULL_RETRY_CYCLE_INTERVAL = TimeDuration.of( 100, TimeDuration.Unit.MILLISECONDS );
-    private static final TimeDuration CLOSE_RETRY_CYCLE_INTERVAL = TimeDuration.of( 100, TimeDuration.Unit.MILLISECONDS );
+    private static final TimeDuration CLOSE_RETRY_CYCLE_INTERVAL = TimeDuration.of( 5, TimeDuration.Unit.MILLISECONDS );
 
     private final Deque<String> queue;
     private final Settings settings;
@@ -350,10 +350,15 @@ public final class WorkQueueProcessor<W extends Serializable>
             logger.trace( () -> "shutdown flag set" );
             notifyWorkPending();
 
-            // rest until not running for up to 3 seconds....
+            // rest until not running for up to 10 seconds....
             if ( running.get() )
             {
-                TimeDuration.of( 3, TimeDuration.Unit.SECONDS ).pause( TimeDuration.of( 10, TimeDuration.Unit.MILLISECONDS ), () -> !running.get() );
+                logger.trace( () -> "running = " + running.get() );
+                final TimeDuration maxWaitTime = TimeDuration.of( 10, TimeDuration.Unit.SECONDS );
+                final Instant startTime = Instant.now();
+                maxWaitTime.of( 10, TimeDuration.Unit.SECONDS ).pause( CLOSE_RETRY_CYCLE_INTERVAL, () -> !running.get() );
+                final TimeDuration waitTime = TimeDuration.fromCurrent( startTime );
+                logger.trace( () -> "waited " + waitTime.asCompactString() + " workQueueSize=" + queue.size() + " running=" + running.get() );
             }
         }
 

+ 3 - 2
server/src/main/java/password/pwm/util/logging/LocalDBLogger.java

@@ -20,6 +20,7 @@
 
 package password.pwm.util.logging;
 
+import password.pwm.AppAttribute;
 import password.pwm.PwmApplication;
 import password.pwm.config.option.DataStorageMethod;
 import password.pwm.error.PwmException;
@@ -109,14 +110,14 @@ public class LocalDBLogger implements PwmService
 
         if ( pwmApplication != null )
         {
-            final String currentFormat = pwmApplication.readAppAttribute( PwmApplication.AppAttribute.LOCALDB_LOGGER_STORAGE_FORMAT, String.class );
+            final String currentFormat = pwmApplication.readAppAttribute( AppAttribute.LOCALDB_LOGGER_STORAGE_FORMAT, String.class );
             if ( !STORAGE_FORMAT_VERSION.equals( currentFormat ) )
             {
                 LOGGER.warn( () -> "localdb logger is using outdated format, clearing existing records (existing='"
                         + currentFormat + "', current='" + STORAGE_FORMAT_VERSION + "')" );
 
                 localDBListQueue.clear();
-                pwmApplication.writeAppAttribute( PwmApplication.AppAttribute.LOCALDB_LOGGER_STORAGE_FORMAT, STORAGE_FORMAT_VERSION );
+                pwmApplication.writeAppAttribute( AppAttribute.LOCALDB_LOGGER_STORAGE_FORMAT, STORAGE_FORMAT_VERSION );
             }
         }
 

+ 3 - 2
server/src/main/java/password/pwm/util/secure/HttpsServerCertificateManager.java

@@ -33,6 +33,7 @@ import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.operator.ContentSigner;
 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import password.pwm.AppAttribute;
 import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
@@ -209,7 +210,7 @@ public class HttpsServerCertificateManager
             final String cnName = makeSubjectName();
             final KeyStore keyStore = KeyStore.getInstance( "jks" );
             keyStore.load( null, password.getStringValue().toCharArray() );
-            StoredCertData storedCertData = pwmApplication.readAppAttribute( PwmApplication.AppAttribute.HTTPS_SELF_CERT, StoredCertData.class );
+            StoredCertData storedCertData = pwmApplication.readAppAttribute( AppAttribute.HTTPS_SELF_CERT, StoredCertData.class );
             if ( storedCertData != null )
             {
                 if ( !cnName.equals( storedCertData.getX509Certificate().getSubjectDN().getName() ) )
@@ -232,7 +233,7 @@ public class HttpsServerCertificateManager
             if ( storedCertData == null )
             {
                 storedCertData = makeSelfSignedCert( cnName );
-                pwmApplication.writeAppAttribute( PwmApplication.AppAttribute.HTTPS_SELF_CERT, storedCertData );
+                pwmApplication.writeAppAttribute( AppAttribute.HTTPS_SELF_CERT, storedCertData );
             }
 
             keyStore.setKeyEntry(

+ 1 - 0
server/src/main/resources/password/pwm/AppProperty.properties

@@ -219,6 +219,7 @@ localdb.location=LocalDB
 localdb.logWriter.bufferSize=500
 localdb.logWriter.maxBufferWaitMs=60000
 localdb.logWriter.maxTrimSize=5001
+localdb.reloadWhenAppRestarted=false
 macro.randomChar.maxLength=100
 macro.ldapAttr.maxLength=100
 logging.devOutput.enable=false

+ 1 - 1
server/src/test/java/password/pwm/util/localdb/TestHelper.java

@@ -75,6 +75,6 @@ public class TestHelper
                 .setApplicationMode( PwmApplicationMode.READ_ONLY )
                 .setInternalRuntimeInstance( true )
                 .createPwmEnvironment();
-        return new PwmApplication( pwmEnvironment );
+        return PwmApplication.createPwmApplication( pwmEnvironment );
     }
 }