Browse Source

Merge remote-tracking branch 'origin/master' into feature/advanced-search

jrivard@gmail.com 6 years ago
parent
commit
63af41843e
100 changed files with 1149 additions and 1295 deletions
  1. 3 2
      .gitignore
  2. 60 1
      README.md
  3. 55 0
      build/checkstyle-header.xml
  4. 5 1
      build/checkstyle-import.xml
  5. 2 19
      build/checkstyle.xml
  6. 0 0
      build/license-header-java.txt
  7. 0 0
      build/license-header-js.txt
  8. 0 0
      build/license-header-jsp.txt
  9. 0 0
      build/license-header-properties.txt
  10. 2 1
      build/license-header-xml.txt
  11. 0 0
      build/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/2013.04.18/ChallengeResponseLCM-2013.04.18.jar
  12. 0 0
      build/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/2013.04.18/ChallengeResponseLCM-2013.04.18.jar.md5
  13. 0 0
      build/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/2013.04.18/ChallengeResponseLCM-2013.04.18.jar.sha1
  14. 0 0
      build/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/2013.04.18/ChallengeResponseLCM-2013.04.18.pom
  15. 0 0
      build/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/2013.04.18/ChallengeResponseLCM-2013.04.18.pom.md5
  16. 0 0
      build/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/2013.04.18/ChallengeResponseLCM-2013.04.18.pom.sha1
  17. 0 0
      build/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/maven-metadata.xml
  18. 0 0
      build/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/maven-metadata.xml.md5
  19. 0 0
      build/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/maven-metadata.xml.sha1
  20. 0 0
      build/local-maven-repo/com/novell/security/nmas/NMASToolkit/2013.04.26/NMASToolkit-2013.04.26.jar
  21. 0 0
      build/local-maven-repo/com/novell/security/nmas/NMASToolkit/2013.04.26/NMASToolkit-2013.04.26.jar.md5
  22. 0 0
      build/local-maven-repo/com/novell/security/nmas/NMASToolkit/2013.04.26/NMASToolkit-2013.04.26.jar.sha1
  23. 0 0
      build/local-maven-repo/com/novell/security/nmas/NMASToolkit/2013.04.26/NMASToolkit-2013.04.26.pom
  24. 0 0
      build/local-maven-repo/com/novell/security/nmas/NMASToolkit/2013.04.26/NMASToolkit-2013.04.26.pom.md5
  25. 0 0
      build/local-maven-repo/com/novell/security/nmas/NMASToolkit/2013.04.26/NMASToolkit-2013.04.26.pom.sha1
  26. 0 0
      build/local-maven-repo/com/novell/security/nmas/NMASToolkit/maven-metadata.xml
  27. 0 0
      build/local-maven-repo/com/novell/security/nmas/NMASToolkit/maven-metadata.xml.md5
  28. 0 0
      build/local-maven-repo/com/novell/security/nmas/NMASToolkit/maven-metadata.xml.sha1
  29. 0 0
      build/local-maven-repo/com/novell/security/nmas/ldap/2013.04.26/ldap-2013.04.26.jar
  30. 0 0
      build/local-maven-repo/com/novell/security/nmas/ldap/2013.04.26/ldap-2013.04.26.jar.md5
  31. 0 0
      build/local-maven-repo/com/novell/security/nmas/ldap/2013.04.26/ldap-2013.04.26.jar.sha1
  32. 0 0
      build/local-maven-repo/com/novell/security/nmas/ldap/2013.04.26/ldap-2013.04.26.pom
  33. 0 0
      build/local-maven-repo/com/novell/security/nmas/ldap/2013.04.26/ldap-2013.04.26.pom.md5
  34. 0 0
      build/local-maven-repo/com/novell/security/nmas/ldap/2013.04.26/ldap-2013.04.26.pom.sha1
  35. 0 0
      build/local-maven-repo/com/novell/security/nmas/ldap/maven-metadata.xml
  36. 0 0
      build/local-maven-repo/com/novell/security/nmas/ldap/maven-metadata.xml.md5
  37. 0 0
      build/local-maven-repo/com/novell/security/nmas/ldap/maven-metadata.xml.sha1
  38. 0 0
      build/local-maven-repo/com/novell/security/nmas/nmasclient/2013.04.26/nmasclient-2013.04.26.jar
  39. 0 0
      build/local-maven-repo/com/novell/security/nmas/nmasclient/2013.04.26/nmasclient-2013.04.26.jar.md5
  40. 0 0
      build/local-maven-repo/com/novell/security/nmas/nmasclient/2013.04.26/nmasclient-2013.04.26.jar.sha1
  41. 0 0
      build/local-maven-repo/com/novell/security/nmas/nmasclient/2013.04.26/nmasclient-2013.04.26.pom
  42. 0 0
      build/local-maven-repo/com/novell/security/nmas/nmasclient/2013.04.26/nmasclient-2013.04.26.pom.md5
  43. 0 0
      build/local-maven-repo/com/novell/security/nmas/nmasclient/2013.04.26/nmasclient-2013.04.26.pom.sha1
  44. 0 0
      build/local-maven-repo/com/novell/security/nmas/nmasclient/maven-metadata.xml
  45. 0 0
      build/local-maven-repo/com/novell/security/nmas/nmasclient/maven-metadata.xml.md5
  46. 0 0
      build/local-maven-repo/com/novell/security/nmas/nmasclient/maven-metadata.xml.sha1
  47. 4 0
      build/spotbugs-exclude.xml
  48. 1 1
      client/package.json
  49. 8 24
      client/pom.xml
  50. 4 1
      client/src/components/changepassword/autogen-change-password.controller.ts
  51. 7 5
      client/src/components/changepassword/success-change-password.component.html
  52. 7 2
      client/src/components/changepassword/success-change-password.controller.ts
  53. 4 1
      client/src/components/changepassword/type-change-password.controller.ts
  54. 1 1
      client/src/i18n/translations_en.json
  55. 10 4
      client/src/modules/helpdesk/helpdesk-detail.component.html
  56. 19 7
      client/src/modules/helpdesk/helpdesk-detail.component.ts
  57. 15 1
      client/src/modules/helpdesk/helpdesk-search-base.component.ts
  58. 1 1
      client/src/modules/helpdesk/helpdesk-search-cards.component.html
  59. 3 3
      client/src/modules/helpdesk/helpdesk-search-cards.component.ts
  60. 1 1
      client/src/modules/helpdesk/helpdesk-search-table.component.html
  61. 3 3
      client/src/modules/helpdesk/helpdesk-search-table.component.ts
  62. 1 0
      client/src/modules/peoplesearch/person-details-dialog.component.scss
  63. 1 1
      client/src/services/helpdesk-config.service.test-data.ts
  64. 1 0
      client/src/services/helpdesk.service.test-data.ts
  65. 1 0
      client/src/services/local-storage.service.ts
  66. 1 2
      client/src/services/object.service.ts
  67. 1 2
      client/src/services/people.service.dev.ts
  68. 1 2
      client/src/services/promise.service.ts
  69. 0 2
      data-service/README.md
  70. 0 325
      data-service/checkstyle.xml
  71. 9 158
      data-service/pom.xml
  72. 13 9
      data-service/src/main/java/password/pwm/receiver/ContextManager.java
  73. 5 5
      data-service/src/main/java/password/pwm/receiver/CsvDownloadServlet.java
  74. 81 57
      data-service/src/main/java/password/pwm/receiver/FtpDataIngestor.java
  75. 11 8
      data-service/src/main/java/password/pwm/receiver/Logger.java
  76. 37 23
      data-service/src/main/java/password/pwm/receiver/PwmReceiverApp.java
  77. 33 6
      data-service/src/main/java/password/pwm/receiver/PwmReceiverLogger.java
  78. 33 24
      data-service/src/main/java/password/pwm/receiver/Settings.java
  79. 3 3
      data-service/src/main/java/password/pwm/receiver/Status.java
  80. 95 56
      data-service/src/main/java/password/pwm/receiver/Storage.java
  81. 100 82
      data-service/src/main/java/password/pwm/receiver/SummaryBean.java
  82. 39 27
      data-service/src/main/java/password/pwm/receiver/TelemetryRestReceiver.java
  83. 15 14
      data-service/src/main/java/password/pwm/receiver/TelemetryViewerServlet.java
  84. 0 0
      data-service/src/main/resources/password/pwm/receiver/package-info.java
  85. 1 5
      data-service/src/main/webapp/META-INF/context.xml
  86. 22 27
      data-service/src/main/webapp/WEB-INF/jsp/telemetry-viewer.jsp
  87. 3 13
      data-service/src/main/webapp/WEB-INF/web.xml
  88. 20 21
      data-service/src/main/webapp/index.jsp
  89. 41 47
      docker/pom.xml
  90. 0 11
      docker/readme.txt
  91. 28 0
      docker/src/main/java/password/pwm/docker/DockerStub.java
  92. 1 0
      docker/src/main/jib/config/readme.txt
  93. 1 1
      onejar/onejar-assembly.xml
  94. 7 38
      onejar/pom.xml
  95. 68 41
      onejar/src/main/java/password/pwm/onejar/ArgumentParser.java
  96. 58 0
      onejar/src/main/java/password/pwm/onejar/OnejarConfig.java
  97. 2 2
      onejar/src/main/java/password/pwm/onejar/OnejarException.java
  98. 83 0
      onejar/src/main/java/password/pwm/onejar/OnejarMain.java
  99. 0 107
      onejar/src/main/java/password/pwm/onejar/TomcatConfig.java
  100. 118 97
      onejar/src/main/java/password/pwm/onejar/TomcatOnejarRunner.java

+ 3 - 2
.gitignore

@@ -6,8 +6,8 @@
 /.classpath
 /.classpath
 
 
 # IntelliJ Project Files
 # IntelliJ Project Files
-.idea/
-*.iml
+**/.idea/
+**/*.iml
 */.idea/
 */.idea/
 */*.iml
 */*.iml
 
 
@@ -20,3 +20,4 @@
 */target
 */target
 
 
 
 
+/target

+ 60 - 1
README.md

@@ -10,7 +10,7 @@ Official project page is at [https://github.com/pwm-project/pwm/](https://github
 * [Current Builds](https://www.pwm-project.org/artifacts/pwm/) - Current downloads built from recent github project commits
 * [Current Builds](https://www.pwm-project.org/artifacts/pwm/) - Current downloads built from recent github project commits
 * [PWM Reference](https://www.pwm-project.org/pwm/public/reference/) - Reference documentation built into PWM.
 * [PWM Reference](https://www.pwm-project.org/pwm/public/reference/) - Reference documentation built into PWM.
 
 
-Features
+# Features
 * Web based configuration manager with over 400 configurable settings
 * Web based configuration manager with over 400 configurable settings
 * Configurable display values for every user-facing text string
 * Configurable display values for every user-facing text string
 * Localized for Chinese (中文), Czech (ceština), Dutch (Nederlands), English, Finnish (suomi), French (français), German (Deutsch), Hebrew (עברית), Italian (italiano), Japanese (日本語), Korean (한국어), Polish (polski), Portuguese (português), Slovak (Slovenčina), Spanish (español), Thai (ไทย) and Turkish (Türkçe)
 * Localized for Chinese (中文), Czech (ceština), Dutch (Nederlands), English, Finnish (suomi), French (français), German (Deutsch), Hebrew (עברית), Italian (italiano), Japanese (日本語), Korean (한국어), Polish (polski), Portuguese (português), Slovak (Slovenčina), Spanish (español), Thai (ไทย) and Turkish (Türkçe)
@@ -47,3 +47,62 @@ Features
   * OpenLDAP
   * OpenLDAP
 
 
 [NetIQ SSPR](https://www.netiq.com/products/self-service-password-reset/) is a commercial, supported self service password reset product based on PWM.
 [NetIQ SSPR](https://www.netiq.com/products/self-service-password-reset/) is a commercial, supported self service password reset product based on PWM.
+
+# Build Information
+
+Build pre-requisites:
+* Java 1.8 JDK or newer
+* Maven 3.2 or newer
+
+Build execution:
+* Set `JAVA_HOME` environment variable to JDK home  
+* Run `mvn clean package` in base directory
+
+A WAR file suitable for deployment on Apache Tomcat is created in `webapp/target` directory.  Rename to `pwm.war` and copy into `tomcat/webapp` directory.
+
+Alternatively, an executable JAR file is created in `onejar\target`.  This JAR file is self-contained single executable with embedded Apache Tomcat runtime. To execute use a command similar to:   
+
+`java -jar pwm-onejar.jar`
+
+The executable will show additional options that may be required.
+
+A docker image is created in `docker/target` as jib-image.tar.  You can import this docker image using a command similar to:
+
+`docker load --input=jib-image.tar`
+
+Create docker container and run using:
+`docker run -d --name <container name> -p 8443:8443 pwm/pwm-webapp`
+
+This will expose the https port to 8443.  If you want the configuration to persist to you can also exposed configuration volume of `/config` using the docker `-v` option during the container
+creation and map it to a directory on the docker host or use a docker volume container.  
+The PWM docker container will place all of it's configuration and runtime data in the `/config` volume.
+
+
+# PWM Source Code License Update Plan for 2019
+
+* Current License: GPL v2.0
+* New License: Apache 2.0
+* Notice Date: October 1, 2018
+* Update Date: February 1, 2019.
+
+The existing project is licensed using GPL v2.0 License 
+(https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).  On or about the update date, 
+the codebase will be updated as Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0).  
+This change is being made to allow commercial organizations a more friendly license structure.  
+
+This will not change the nature or availability of the PWM Project.  The development and open-source
+nature of PWM will continue as before.   Existing, published PWM source code will continue to be 
+available under the GPL v2.0 License, however all new development after the license date will only
+be published using the new license type.
+
+At the time of the license update, the existing source code will be branched and the code source
+will be archived and published under the existing GPL v2.0 license terms in perpetuity.  
+
+Process:
+* This notice is posted publicly
+* Known contributors are sent this notice using last known address
+* Any objections or concerns by contributors will be processed as appropriate
+* On or about the update date:
+  * The existing code is branched and the GPL version will remain available
+  * License headers and notices will be updated to the new license type
+  * New development and contributions will be done under the new license type

+ 55 - 0
build/checkstyle-header.xml

@@ -0,0 +1,55 @@
+<?xml version="1.0"?>
+<!--
+  ~ Password Management Servlets (PWM)
+  ~ http://www.pwm-project.org
+  ~
+  ~ Copyright (c) 2006-2009 Novell, Inc.
+  ~ Copyright (c) 2009-2018 The PWM Project
+  ~
+  ~ This program is free software; you can redistribute it and/or modify
+  ~ it under the terms of the GNU General Public License as published by
+  ~ the Free Software Foundation; either version 2 of the License, or
+  ~ (at your option) any later version.
+  ~
+  ~ This program is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  ~ GNU General Public License for more details.
+  ~
+  ~ You should have received a copy of the GNU General Public License
+  ~ along with this program; if not, write to the Free Software
+  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+  -->
+
+<!DOCTYPE module PUBLIC
+        "-//Puppy Crawl//DTD Check Configuration 1.2//EN"
+        "http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
+
+<!--
+  PWM Checkstyle definition
+-->
+
+<module name="Checker">
+
+    <module name="Header">
+        <property name="headerFile" value="${basedir}/build/license-header-jsp.txt"/>
+        <property name="fileExtensions" value="jsp"/>
+    </module>
+    <module name="Header">
+        <property name="headerFile" value="${basedir}/build/license-header-java.txt"/>
+        <property name="fileExtensions" value="java,css"/>
+    </module>
+    <module name="Header">
+        <property name="headerFile" value="${basedir}/build/license-header-xml.txt"/>
+        <property name="fileExtensions" value="xml,svg"/>
+    </module>
+    <module name="Header">
+        <property name="headerFile" value="${basedir}/build/license-header-properties.txt"/>
+        <property name="fileExtensions" value="properties"/>
+    </module>
+    <module name="Header">
+        <property name="headerFile" value="${basedir}/build/license-header-js.txt"/>
+        <property name="fileExtensions" value="js,ts"/>
+    </module>
+
+</module>

+ 5 - 1
server/src/build/checkstyle-import.xml → build/checkstyle-import.xml

@@ -37,6 +37,8 @@
     <allow pkg="java.lang"/>
     <allow pkg="java.lang"/>
     <allow pkg="java.security"/>
     <allow pkg="java.security"/>
 
 
+    <allow pkg="org.apache.catalina"/>
+
     <!-- chai -->
     <!-- chai -->
     <allow pkg="com.novell.ldapchai"/>
     <allow pkg="com.novell.ldapchai"/>
 
 
@@ -58,6 +60,8 @@
     <!-- gson -->
     <!-- gson -->
     <allow pkg="com.google.gson"/>
     <allow pkg="com.google.gson"/>
 
 
+    <allow pkg="javax.management"/>
+
     <!-- to be removed/scoped -->
     <!-- to be removed/scoped -->
     <allow pkg="org.apache.http"/>
     <allow pkg="org.apache.http"/>
     <allow pkg="org.apache.commons"/>
     <allow pkg="org.apache.commons"/>
@@ -83,6 +87,7 @@
 
 
     <allow pkg="edu.umd.cs.findbugs.annotations"/>
     <allow pkg="edu.umd.cs.findbugs.annotations"/>
     <allow pkg="com.nulabinc.zxcvbn" />
     <allow pkg="com.nulabinc.zxcvbn" />
+    <allow pkg="jetbrains.exodus"/>
 
 
     <subpackage name="password.pwm.util">
     <subpackage name="password.pwm.util">
         <allow pkg="sun.management"/>
         <allow pkg="sun.management"/>
@@ -125,7 +130,6 @@
     </subpackage>
     </subpackage>
 
 
     <subpackage name="util.localdb">
     <subpackage name="util.localdb">
-        <allow pkg="jetbrains.exodus"/>
         <allow pkg="java.sql"/>
         <allow pkg="java.sql"/>
     </subpackage>
     </subpackage>
 
 

+ 2 - 19
server/src/build/checkstyle.xml → build/checkstyle.xml

@@ -31,23 +31,6 @@
 
 
 <module name="Checker">
 <module name="Checker">
 
 
-    <module name="Header">
-        <property name="headerFile" value="${basedir}/src/build/license-header-jsp.txt"/>
-        <property name="fileExtensions" value="jsp"/>
-    </module>
-    <module name="Header">
-        <property name="headerFile" value="${basedir}/src/build/license-header-java.txt"/>
-        <property name="fileExtensions" value="java,css"/>
-    </module>
-    <module name="Header">
-        <property name="headerFile" value="${basedir}/src/build/license-header-xml.txt"/>
-        <property name="fileExtensions" value="xml,svg"/>
-    </module>
-    <module name="Header">
-        <property name="headerFile" value="${basedir}/src/build/license-header-properties.txt"/>
-        <property name="fileExtensions" value="properties"/>
-    </module>
-
 
 
     <!-- Checks that each Java package has a Javadoc file used for commenting. -->
     <!-- Checks that each Java package has a Javadoc file used for commenting. -->
     <!-- See http://checkstyle.sf.net/config_javadoc.html#JavadocPackage       -->
     <!-- See http://checkstyle.sf.net/config_javadoc.html#JavadocPackage       -->
@@ -76,7 +59,7 @@
         <property name="lineSeparator" value="lf_cr_crlf" />
         <property name="lineSeparator" value="lf_cr_crlf" />
     </module>
     </module>
 
 
-    <module name="TreeWalker">
+    <module name="TreeWalker" >
         <property name="cacheFile" value="target/checkstyle.cache"/>
         <property name="cacheFile" value="target/checkstyle.cache"/>
 
 
         <!-- required for SuppressWarningsFilter (and other Suppress* rules not used here) -->
         <!-- required for SuppressWarningsFilter (and other Suppress* rules not used here) -->
@@ -179,7 +162,7 @@
         <module name="RedundantImport"/>
         <module name="RedundantImport"/>
         <module name="UnusedImports"/>
         <module name="UnusedImports"/>
         <module name="ImportControl">
         <module name="ImportControl">
-            <property name="file" value="${basedir}/src/build/checkstyle-import.xml"/>
+            <property name="file" value="${basedir}/build/checkstyle-import.xml"/>
         </module>
         </module>
 
 
 
 

+ 0 - 0
server/src/build/license-header-java.txt → build/license-header-java.txt


+ 0 - 0
server/src/main/webapp/public/resources/themes/matrix/mobileStyle.css → build/license-header-js.txt


+ 0 - 0
server/src/build/license-header-jsp.txt → build/license-header-jsp.txt


+ 0 - 0
server/src/build/license-header-properties.txt → build/license-header-properties.txt


+ 2 - 1
server/src/build/license-header-xml.txt → build/license-header-xml.txt

@@ -1,7 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 <!--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
-  ~ Copyright (c) 2006-2018 Novell, Inc.
+  ~ Copyright (c) 2006-2009 Novell, Inc.
+  ~ Copyright (c) 2009-2018 The PWM Project
   ~
   ~
   ~ This program is free software; you can redistribute it and/or modify
   ~ This program is free software; you can redistribute it and/or modify
   ~ it under the terms of the GNU General Public License as published by
   ~ it under the terms of the GNU General Public License as published by

+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/2013.04.18/ChallengeResponseLCM-2013.04.18.jar → build/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/2013.04.18/ChallengeResponseLCM-2013.04.18.jar


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/2013.04.18/ChallengeResponseLCM-2013.04.18.jar.md5 → build/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/2013.04.18/ChallengeResponseLCM-2013.04.18.jar.md5


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/2013.04.18/ChallengeResponseLCM-2013.04.18.jar.sha1 → build/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/2013.04.18/ChallengeResponseLCM-2013.04.18.jar.sha1


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/2013.04.18/ChallengeResponseLCM-2013.04.18.pom → build/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/2013.04.18/ChallengeResponseLCM-2013.04.18.pom


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/2013.04.18/ChallengeResponseLCM-2013.04.18.pom.md5 → build/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/2013.04.18/ChallengeResponseLCM-2013.04.18.pom.md5


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/2013.04.18/ChallengeResponseLCM-2013.04.18.pom.sha1 → build/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/2013.04.18/ChallengeResponseLCM-2013.04.18.pom.sha1


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/maven-metadata.xml → build/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/maven-metadata.xml


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/maven-metadata.xml.md5 → build/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/maven-metadata.xml.md5


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/maven-metadata.xml.sha1 → build/local-maven-repo/com/novell/security/nmas/ChallengeResponseLCM/maven-metadata.xml.sha1


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/NMASToolkit/2013.04.26/NMASToolkit-2013.04.26.jar → build/local-maven-repo/com/novell/security/nmas/NMASToolkit/2013.04.26/NMASToolkit-2013.04.26.jar


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/NMASToolkit/2013.04.26/NMASToolkit-2013.04.26.jar.md5 → build/local-maven-repo/com/novell/security/nmas/NMASToolkit/2013.04.26/NMASToolkit-2013.04.26.jar.md5


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/NMASToolkit/2013.04.26/NMASToolkit-2013.04.26.jar.sha1 → build/local-maven-repo/com/novell/security/nmas/NMASToolkit/2013.04.26/NMASToolkit-2013.04.26.jar.sha1


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/NMASToolkit/2013.04.26/NMASToolkit-2013.04.26.pom → build/local-maven-repo/com/novell/security/nmas/NMASToolkit/2013.04.26/NMASToolkit-2013.04.26.pom


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/NMASToolkit/2013.04.26/NMASToolkit-2013.04.26.pom.md5 → build/local-maven-repo/com/novell/security/nmas/NMASToolkit/2013.04.26/NMASToolkit-2013.04.26.pom.md5


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/NMASToolkit/2013.04.26/NMASToolkit-2013.04.26.pom.sha1 → build/local-maven-repo/com/novell/security/nmas/NMASToolkit/2013.04.26/NMASToolkit-2013.04.26.pom.sha1


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/NMASToolkit/maven-metadata.xml → build/local-maven-repo/com/novell/security/nmas/NMASToolkit/maven-metadata.xml


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/NMASToolkit/maven-metadata.xml.md5 → build/local-maven-repo/com/novell/security/nmas/NMASToolkit/maven-metadata.xml.md5


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/NMASToolkit/maven-metadata.xml.sha1 → build/local-maven-repo/com/novell/security/nmas/NMASToolkit/maven-metadata.xml.sha1


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/ldap/2013.04.26/ldap-2013.04.26.jar → build/local-maven-repo/com/novell/security/nmas/ldap/2013.04.26/ldap-2013.04.26.jar


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/ldap/2013.04.26/ldap-2013.04.26.jar.md5 → build/local-maven-repo/com/novell/security/nmas/ldap/2013.04.26/ldap-2013.04.26.jar.md5


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/ldap/2013.04.26/ldap-2013.04.26.jar.sha1 → build/local-maven-repo/com/novell/security/nmas/ldap/2013.04.26/ldap-2013.04.26.jar.sha1


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/ldap/2013.04.26/ldap-2013.04.26.pom → build/local-maven-repo/com/novell/security/nmas/ldap/2013.04.26/ldap-2013.04.26.pom


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/ldap/2013.04.26/ldap-2013.04.26.pom.md5 → build/local-maven-repo/com/novell/security/nmas/ldap/2013.04.26/ldap-2013.04.26.pom.md5


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/ldap/2013.04.26/ldap-2013.04.26.pom.sha1 → build/local-maven-repo/com/novell/security/nmas/ldap/2013.04.26/ldap-2013.04.26.pom.sha1


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/ldap/maven-metadata.xml → build/local-maven-repo/com/novell/security/nmas/ldap/maven-metadata.xml


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/ldap/maven-metadata.xml.md5 → build/local-maven-repo/com/novell/security/nmas/ldap/maven-metadata.xml.md5


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/ldap/maven-metadata.xml.sha1 → build/local-maven-repo/com/novell/security/nmas/ldap/maven-metadata.xml.sha1


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/nmasclient/2013.04.26/nmasclient-2013.04.26.jar → build/local-maven-repo/com/novell/security/nmas/nmasclient/2013.04.26/nmasclient-2013.04.26.jar


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/nmasclient/2013.04.26/nmasclient-2013.04.26.jar.md5 → build/local-maven-repo/com/novell/security/nmas/nmasclient/2013.04.26/nmasclient-2013.04.26.jar.md5


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/nmasclient/2013.04.26/nmasclient-2013.04.26.jar.sha1 → build/local-maven-repo/com/novell/security/nmas/nmasclient/2013.04.26/nmasclient-2013.04.26.jar.sha1


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/nmasclient/2013.04.26/nmasclient-2013.04.26.pom → build/local-maven-repo/com/novell/security/nmas/nmasclient/2013.04.26/nmasclient-2013.04.26.pom


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/nmasclient/2013.04.26/nmasclient-2013.04.26.pom.md5 → build/local-maven-repo/com/novell/security/nmas/nmasclient/2013.04.26/nmasclient-2013.04.26.pom.md5


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/nmasclient/2013.04.26/nmasclient-2013.04.26.pom.sha1 → build/local-maven-repo/com/novell/security/nmas/nmasclient/2013.04.26/nmasclient-2013.04.26.pom.sha1


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/nmasclient/maven-metadata.xml → build/local-maven-repo/com/novell/security/nmas/nmasclient/maven-metadata.xml


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/nmasclient/maven-metadata.xml.md5 → build/local-maven-repo/com/novell/security/nmas/nmasclient/maven-metadata.xml.md5


+ 0 - 0
server/local-maven-repo/com/novell/security/nmas/nmasclient/maven-metadata.xml.sha1 → build/local-maven-repo/com/novell/security/nmas/nmasclient/maven-metadata.xml.sha1


+ 4 - 0
server/src/build/spotbugs-exclude.xml → build/spotbugs-exclude.xml

@@ -7,4 +7,8 @@
         <!-- due to bug https://github.com/spotbugs/spotbugs/issues/493 in spotbugs 3.1.3 -->
         <!-- due to bug https://github.com/spotbugs/spotbugs/issues/493 in spotbugs 3.1.3 -->
         <Bug pattern="OBL_UNSATISFIED_OBLIGATION"/>
         <Bug pattern="OBL_UNSATISFIED_OBLIGATION"/>
     </Match>
     </Match>
+    <Match>
+        <!-- due to bug with java 11 -->
+        <Bug pattern="RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE"/>
+    </Match>
 </FindBugsFilter>
 </FindBugsFilter>

+ 1 - 1
client/package.json

@@ -13,7 +13,7 @@
         "test": "karma start test/karma.conf.js --mode=development",
         "test": "karma start test/karma.conf.js --mode=development",
         "test-single-run": "karma start test/karma.conf.js --mode=development --singleRun --no-auto-watch",
         "test-single-run": "karma start test/karma.conf.js --mode=development --singleRun --no-auto-watch",
         "start": "webpack-dev-server --mode=development --port 4000 --history-api-fallback --colors",
         "start": "webpack-dev-server --mode=development --port 4000 --history-api-fallback --colors",
-        "sync": "webpack --mode=production --output-path=../server/target/pwm-1.8.0-SNAPSHOT/public/resources/webjars/pwm-client --watch --progress --colors"
+        "sync": "webpack --mode=production --output-path=../webapp/target/pwm-1.8.0-SNAPSHOT/public/resources/webjars/pwm-client --watch --progress --colors"
     },
     },
     "author": "",
     "author": "",
     "license": "ISC",
     "license": "ISC",

+ 8 - 24
client/pom.xml

@@ -14,31 +14,15 @@
 
 
     <name>PWM Password Self Service: Client</name>
     <name>PWM Password Self Service: Client</name>
 
 
+    <properties>
+        <project.root.basedir>${project.basedir}/..</project.root.basedir>
+    </properties>
+
     <build>
     <build>
         <plugins>
         <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-enforcer-plugin</artifactId>
-                <version>3.0.0-M1</version>
-                <executions>
-                    <execution>
-                        <id>enforce-maven</id>
-                        <goals>
-                            <goal>enforce</goal>
-                        </goals>
-                        <configuration>
-                            <rules>
-                                <requireMavenVersion>
-                                    <version>${pwm.minimum.maven.version}</version>
-                                </requireMavenVersion>
-                            </rules>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
             <plugin>
             <plugin>
                 <artifactId>maven-resources-plugin</artifactId>
                 <artifactId>maven-resources-plugin</artifactId>
-                <version>3.0.2</version>
+                <version>3.1.0</version>
                 <executions>
                 <executions>
                     <execution>
                     <execution>
                         <id>copy-client-files</id>
                         <id>copy-client-files</id>
@@ -59,7 +43,7 @@
             </plugin>
             </plugin>
             <plugin>
             <plugin>
                 <artifactId>maven-clean-plugin</artifactId>
                 <artifactId>maven-clean-plugin</artifactId>
-                <version>3.0.0</version>
+                <version>3.1.0</version>
                 <executions>
                 <executions>
                     <execution>
                     <execution>
                         <id>remove-client-files</id>
                         <id>remove-client-files</id>
@@ -86,7 +70,7 @@
             <plugin>
             <plugin>
                 <groupId>com.github.eirslett</groupId>
                 <groupId>com.github.eirslett</groupId>
                 <artifactId>frontend-maven-plugin</artifactId>
                 <artifactId>frontend-maven-plugin</artifactId>
-                <version>1.5</version>
+                <version>1.6</version>
                 <configuration>
                 <configuration>
                     <nodeVersion>v8.9.4</nodeVersion>
                     <nodeVersion>v8.9.4</nodeVersion>
                     <npmVersion>5.6.0</npmVersion>
                     <npmVersion>5.6.0</npmVersion>
@@ -122,7 +106,7 @@
             <plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-jar-plugin</artifactId>
                 <artifactId>maven-jar-plugin</artifactId>
-                <version>3.0.2</version>
+                <version>3.1.0</version>
                 <configuration>
                 <configuration>
                     <archive>
                     <archive>
                         <manifestEntries>
                         <manifestEntries>

+ 4 - 1
client/src/components/changepassword/autogen-change-password.controller.ts

@@ -38,7 +38,10 @@ export default class AutogenChangePasswordController {
                 private HelpDeskService: IHelpDeskService,
                 private HelpDeskService: IHelpDeskService,
                 private IasDialogService: any,
                 private IasDialogService: any,
                 private personUserKey: string) {
                 private personUserKey: string) {
-        this.passwordSuggestions = Array<string>(20).fill('');
+        this.passwordSuggestions = [];
+        for (let i = 0; i < 20; i++) {
+            this.passwordSuggestions.push('');
+        }
         this.populatePasswordSuggestions();
         this.populatePasswordSuggestions();
     }
     }
 
 

+ 7 - 5
client/src/components/changepassword/success-change-password.component.html

@@ -28,11 +28,13 @@
 
 
         <div class="ias-dialog-content">
         <div class="ias-dialog-content">
             <p ng-bind="$ctrl.successMessage"></p>
             <p ng-bind="$ctrl.successMessage"></p>
-            <span ng-bind="'Field_NewPassword' | translate"></span>
-            <ias-button ng-click="$ctrl.togglePasswordMasked()" ng-if="$ctrl.maskPasswords">
-                {{ 'Button_Show' | translate }}
-            </ias-button>
-            <input ng-model="$ctrl.password" ng-hide="$ctrl.passwordMasked" readonly type="text" autofocus>
+            <div ng-if="$ctrl.displayNewPassword">
+                <span ng-bind="'Field_NewPassword' | translate"></span>
+                <ias-button ng-click="$ctrl.togglePasswordMasked()" ng-if="$ctrl.maskPasswords">
+                    {{ 'Button_Show' | translate }}
+                </ias-button>
+                <input ng-model="$ctrl.password" ng-hide="$ctrl.passwordMasked" readonly type="text" autofocus>
+            </div>
         </div>
         </div>
 
 
         <div class="ias-actions">
         <div class="ias-actions">

+ 7 - 2
client/src/components/changepassword/success-change-password.controller.ts

@@ -23,7 +23,7 @@
 
 
 import {IHelpDeskService } from '../../services/helpdesk.service';
 import {IHelpDeskService } from '../../services/helpdesk.service';
 import {IQService} from 'angular';
 import {IQService} from 'angular';
-import {IHelpDeskConfigService} from '../../services/helpdesk-config.service';
+import {IHelpDeskConfigService, PASSWORD_UI_MODES} from '../../services/helpdesk-config.service';
 
 
 export interface IChangePasswordSuccess {
 export interface IChangePasswordSuccess {
     password: string;
     password: string;
@@ -36,6 +36,7 @@ export default class SuccessChangePasswordController {
     password: string;
     password: string;
     passwordMasked: boolean;
     passwordMasked: boolean;
     successMessage: string;
     successMessage: string;
+    displayNewPassword: boolean;
 
 
     static $inject = [
     static $inject = [
         '$q',
         '$q',
@@ -58,12 +59,16 @@ export default class SuccessChangePasswordController {
 
 
         let promise = this.$q.all([
         let promise = this.$q.all([
             this.configService.getClearResponsesSetting(),
             this.configService.getClearResponsesSetting(),
-            this.configService.maskPasswordsEnabled()
+            this.configService.maskPasswordsEnabled(),
+            this.configService.getPasswordUiMode()
         ]);
         ]);
         promise.then((result) => {
         promise.then((result) => {
             this.clearResponsesSetting = result[0];
             this.clearResponsesSetting = result[0];
             this.maskPasswords = result[1];
             this.maskPasswords = result[1];
             this.passwordMasked = this.maskPasswords;
             this.passwordMasked = this.maskPasswords;
+
+            // If it's random, don't display the new password
+            this.displayNewPassword = (result[2] !== PASSWORD_UI_MODES.RANDOM);
         });
         });
     }
     }
 
 

+ 4 - 1
client/src/components/changepassword/type-change-password.controller.ts

@@ -78,7 +78,10 @@ export default class TypeChangePasswordController {
         this.password1 = '';
         this.password1 = '';
         this.password2 = '';
         this.password2 = '';
         this.passwordAcceptable = false;
         this.passwordAcceptable = false;
-        this.passwordSuggestions = Array<string>(20).fill('');
+        this.passwordSuggestions = [];
+        for (let i = 0; i < 20; i++) {
+            this.passwordSuggestions.push('');
+        }
         this.pendingValidation = false;
         this.pendingValidation = false;
         this.showStrengthMeter = HelpDeskService.showStrengthMeter;
         this.showStrengthMeter = HelpDeskService.showStrengthMeter;
         this.strength = 0;
         this.strength = 0;

+ 1 - 1
client/src/i18n/translations_en.json

@@ -52,7 +52,7 @@
   "Title_AdvancedSearch": "Advanced Search",
   "Title_AdvancedSearch": "Advanced Search",
   "Title_ChangePassword": "Change Password",
   "Title_ChangePassword": "Change Password",
   "Title_DirectReports": "Direct Report(s)",
   "Title_DirectReports": "Direct Report(s)",
-  "Title_HelpDesk": "Help Desk",
+  "Title_Helpdesk": "Help Desk",
   "Title_HelpDeskCard": "Help Desk Cards",
   "Title_HelpDeskCard": "Help Desk Cards",
   "Title_HelpDeskTable": "Help Desk Table",
   "Title_HelpDeskTable": "Help Desk Table",
   "Title_Management": "Management",
   "Title_Management": "Management",

+ 10 - 4
client/src/modules/helpdesk/helpdesk-detail.component.html

@@ -21,7 +21,7 @@
   -->
   -->
 
 
 <div class="ias-header">
 <div class="ias-header">
-    <h2 id="page-content-title" translate="Title_HelpDesk">Help Desk</h2>
+    <h2 id="page-content-title" translate="Title_Helpdesk">Help Desk</h2>
     <span class="ias-fill"></span>
     <span class="ias-fill"></span>
     <div class="help-desk-icons">
     <div class="help-desk-icons">
         <ias-button id="helpdesk-refresh-icon" class="ias-icon-button"
         <ias-button id="helpdesk-refresh-icon" class="ias-icon-button"
@@ -120,7 +120,9 @@
                         <td ng-bind="item.label"></td>
                         <td ng-bind="item.label"></td>
                         <td ng-bind="item.value | dateFilter" ng-if="item.type==='timestamp'"></td>
                         <td ng-bind="item.value | dateFilter" ng-if="item.type==='timestamp'"></td>
                         <td ng-bind="item.value" ng-if="item.type==='string' || item.type==='number'"></td>
                         <td ng-bind="item.value" ng-if="item.type==='string' || item.type==='number'"></td>
-                        <td ng-bind="value" ng-if="item.type==='multiString'" ng-repeat="value in item.values"></td>
+                        <td ng-if="item.type==='multiString'">
+                            <div ng-bind="value" ng-repeat="value in item.values"></div>
+                        </td>
                     </tr>
                     </tr>
                     </tbody>
                     </tbody>
                 </table>
                 </table>
@@ -132,7 +134,9 @@
                         <td ng-bind="item.label"></td>
                         <td ng-bind="item.label"></td>
                         <td ng-bind="item.value | dateFilter" ng-if="item.type==='timestamp'"></td>
                         <td ng-bind="item.value | dateFilter" ng-if="item.type==='timestamp'"></td>
                         <td ng-bind="item.value" ng-if="item.type==='string' || item.type==='number'"></td>
                         <td ng-bind="item.value" ng-if="item.type==='string' || item.type==='number'"></td>
-                        <td ng-bind="value" ng-if="item.type==='multiString'" ng-repeat="value in item.values"></td>
+                        <td ng-if="item.type==='multiString'">
+                            <div ng-bind="value" ng-repeat="value in item.values"></div>
+                        </td>
                     </tr>
                     </tr>
                     </tbody>
                     </tbody>
                 </table>
                 </table>
@@ -180,7 +184,9 @@
                         <td ng-bind="item.label"></td>
                         <td ng-bind="item.label"></td>
                         <td ng-bind="item.value | dateFilter" ng-if="item.type==='timestamp'"></td>
                         <td ng-bind="item.value | dateFilter" ng-if="item.type==='timestamp'"></td>
                         <td ng-bind="item.value" ng-if="item.type==='string' || item.type==='number'"></td>
                         <td ng-bind="item.value" ng-if="item.type==='string' || item.type==='number'"></td>
-                        <td ng-bind="value" ng-if="item.type==='multiString'" ng-repeat="value in item.values"></td>
+                        <td ng-if="item.type==='multiString'">
+                            <div ng-bind="value" ng-repeat="value in item.values"></div>
+                        </td>
                     </tr>
                     </tr>
                     </tbody>
                     </tbody>
                 </table>
                 </table>

+ 19 - 7
client/src/modules/helpdesk/helpdesk-detail.component.ts

@@ -1,6 +1,6 @@
 /*
 /*
  * Password Management Servlets (PWM)
  * Password Management Servlets (PWM)
-  htt://www.pwm-project.org
+ * http://www.pwm-project.org
  *
  *
  * Copyright (c) 2006-2009 Novell, Inc.
  * Copyright (c) 2006-2009 Novell, Inc.
  * Copyright (c) 2009-2018 The PWM Project
  * Copyright (c) 2009-2018 The PWM Project
@@ -20,7 +20,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
  */
 
 
-
 import {Component} from '../../component';
 import {Component} from '../../component';
 import {IHelpDeskService, ISuccessResponse} from '../../services/helpdesk.service';
 import {IHelpDeskService, ISuccessResponse} from '../../services/helpdesk.service';
 import {IScope, ui} from 'angular';
 import {IScope, ui} from 'angular';
@@ -28,11 +27,14 @@ import {noop} from 'angular';
 import {IActionButton, IHelpDeskConfigService, PASSWORD_UI_MODES} from '../../services/helpdesk-config.service';
 import {IActionButton, IHelpDeskConfigService, PASSWORD_UI_MODES} from '../../services/helpdesk-config.service';
 import {IPerson} from '../../models/person.model';
 import {IPerson} from '../../models/person.model';
 import {IChangePasswordSuccess} from '../../components/changepassword/success-change-password.controller';
 import {IChangePasswordSuccess} from '../../components/changepassword/success-change-password.controller';
+import LocalStorageService from '../../services/local-storage.service';
 
 
-let autogenChangePasswordTemplateUrl = require('../../components/changepassword/autogen-change-password.component.html');
+let autogenChangePasswordTemplateUrl =
+    require('../../components/changepassword/autogen-change-password.component.html');
 let helpdeskDetailDialogTemplateUrl = require('./helpdesk-detail-dialog.template.html');
 let helpdeskDetailDialogTemplateUrl = require('./helpdesk-detail-dialog.template.html');
 let randomChangePasswordTemplateUrl = require('../../components/changepassword/random-change-password.component.html');
 let randomChangePasswordTemplateUrl = require('../../components/changepassword/random-change-password.component.html');
-let successChangePasswordTemplateUrl = require('../../components/changepassword/success-change-password.component.html');
+let successChangePasswordTemplateUrl =
+    require('../../components/changepassword/success-change-password.component.html');
 let typeChangePasswordTemplateUrl = require('../../components/changepassword/type-change-password.component.html');
 let typeChangePasswordTemplateUrl = require('../../components/changepassword/type-change-password.component.html');
 let verificationsDialogTemplateUrl = require('./verifications-dialog.template.html');
 let verificationsDialogTemplateUrl = require('./verifications-dialog.template.html');
 
 
@@ -50,6 +52,7 @@ export default class HelpDeskDetailComponent {
     person: any;
     person: any;
     personCard: IPerson;
     personCard: IPerson;
     photosEnabled: boolean;
     photosEnabled: boolean;
+    searchViewLocalStorageKey: string;
 
 
     static $inject = [
     static $inject = [
         '$state',
         '$state',
@@ -57,7 +60,8 @@ export default class HelpDeskDetailComponent {
         'ConfigService',
         'ConfigService',
         'HelpDeskService',
         'HelpDeskService',
         'IasDialogService',
         'IasDialogService',
-        'IasToggleService'
+        'IasToggleService',
+        'LocalStorageService'
     ];
     ];
 
 
     constructor(private $state: ui.IStateService,
     constructor(private $state: ui.IStateService,
@@ -65,7 +69,9 @@ export default class HelpDeskDetailComponent {
                 private configService: IHelpDeskConfigService,
                 private configService: IHelpDeskConfigService,
                 private helpDeskService: IHelpDeskService,
                 private helpDeskService: IHelpDeskService,
                 private IasDialogService: any,
                 private IasDialogService: any,
-                private toggleService: { showComponent: (componentName: string) => null }) {
+                private toggleService: { showComponent: (componentName: string) => null },
+                private localStorageService: LocalStorageService) {
+        this.searchViewLocalStorageKey = this.localStorageService.keys.HELPDESK_SEARCH_VIEW;
     }
     }
 
 
     $onInit(): void {
     $onInit(): void {
@@ -353,7 +359,13 @@ export default class HelpDeskDetailComponent {
     }
     }
 
 
     gotoSearch(): void {
     gotoSearch(): void {
-        this.$state.go('search.cards');
+        let view = this.localStorageService.getItem(this.searchViewLocalStorageKey);
+        if (view) {
+            this.$state.go(view);
+        }
+        else {
+            this.$state.go('search.cards');
+        }
     }
     }
 
 
     initialize(): void {
     initialize(): void {

+ 15 - 1
client/src/modules/helpdesk/helpdesk-search-base.component.ts

@@ -45,10 +45,10 @@ export default abstract class HelpDeskSearchBaseComponent {
     searchTextLocalStorageKey: string;
     searchTextLocalStorageKey: string;
     searchViewLocalStorageKey: string;
     searchViewLocalStorageKey: string;
     verificationsEnabled: boolean;
     verificationsEnabled: boolean;
-    view: string;
 
 
     constructor(protected $q: IQService,
     constructor(protected $q: IQService,
                 protected $scope: IScope,
                 protected $scope: IScope,
+                protected $state: angular.ui.IStateService,
                 protected $stateParams: angular.ui.IStateParamsService,
                 protected $stateParams: angular.ui.IStateParamsService,
                 protected $timeout: ITimeoutService,
                 protected $timeout: ITimeoutService,
                 protected $translate: angular.translate.ITranslateService,
                 protected $translate: angular.translate.ITranslateService,
@@ -152,6 +152,10 @@ export default abstract class HelpDeskSearchBaseComponent {
             }.bind(this));
             }.bind(this));
     }
     }
 
 
+    private gotoState(state: string): void {
+        this.$state.go(state, { query: this.query });
+    }
+
     private onSearchTextChange(newValue: string, oldValue: string): void {
     private onSearchTextChange(newValue: string, oldValue: string): void {
         if (newValue === oldValue) {
         if (newValue === oldValue) {
             return;
             return;
@@ -230,4 +234,14 @@ export default abstract class HelpDeskSearchBaseComponent {
     protected storeSearchText(): void {
     protected storeSearchText(): void {
         this.localStorageService.setItem(this.searchTextLocalStorageKey, this.query || '');
         this.localStorageService.setItem(this.searchTextLocalStorageKey, this.query || '');
     }
     }
+
+    protected toggleView(state: string): void {
+        this.storeSearchView(state);
+        this.storeSearchText();
+        this.gotoState(state);
+    }
+
+    private storeSearchView(state: string) {
+        this.localStorageService.setItem(this.searchViewLocalStorageKey, state);
+    }
 }
 }

+ 1 - 1
client/src/modules/helpdesk/helpdesk-search-cards.component.html

@@ -21,7 +21,7 @@
   -->
   -->
 
 
 <div class="ias-header">
 <div class="ias-header">
-    <h2 id="page-content-title" translate="Title_HelpDesk">Help Desk</h2>
+    <h2 id="page-content-title" translate="Title_Helpdesk">Help Desk</h2>
     <ias-search-box ng-model="$ctrl.query" ng-model-options="{debounce: $ctrl.inputDebounce}"
     <ias-search-box ng-model="$ctrl.query" ng-model-options="{debounce: $ctrl.inputDebounce}"
                     placeholder="{{'Placeholder_Search' | translate}}">
                     placeholder="{{'Placeholder_Search' | translate}}">
     </ias-search-box>
     </ias-search-box>

+ 3 - 3
client/src/modules/helpdesk/helpdesk-search-cards.component.ts

@@ -53,7 +53,7 @@ export default class HelpDeskSearchCardsComponent extends HelpDeskSearchBaseComp
     ];
     ];
     constructor($q: IQService,
     constructor($q: IQService,
                 $scope: IScope,
                 $scope: IScope,
-                private $state: angular.ui.IStateService,
+                $state: angular.ui.IStateService,
                 $stateParams: angular.ui.IStateParamsService,
                 $stateParams: angular.ui.IStateParamsService,
                 $timeout: ITimeoutService,
                 $timeout: ITimeoutService,
                 $translate: angular.translate.ITranslateService,
                 $translate: angular.translate.ITranslateService,
@@ -63,7 +63,7 @@ export default class HelpDeskSearchCardsComponent extends HelpDeskSearchBaseComp
                 localStorageService: LocalStorageService,
                 localStorageService: LocalStorageService,
                 promiseService: PromiseService,
                 promiseService: PromiseService,
                 pwmService: IPwmService) {
                 pwmService: IPwmService) {
-        super($q, $scope, $stateParams, $timeout, $translate, configService, helpDeskService, IasDialogService,
+        super($q, $scope, $state, $stateParams, $timeout, $translate, configService, helpDeskService, IasDialogService,
             localStorageService, promiseService, pwmService);
             localStorageService, promiseService, pwmService);
     }
     }
 
 
@@ -84,7 +84,7 @@ export default class HelpDeskSearchCardsComponent extends HelpDeskSearchBaseComp
     }
     }
 
 
     gotoTableView(): void {
     gotoTableView(): void {
-        this.$state.go('search.table', {query: this.query});
+        this.toggleView('search.table');
     }
     }
 
 
     private onSearchResult(searchResult: SearchResult): void {
     private onSearchResult(searchResult: SearchResult): void {

+ 1 - 1
client/src/modules/helpdesk/helpdesk-search-table.component.html

@@ -21,7 +21,7 @@
   -->
   -->
 
 
 <div class="ias-header">
 <div class="ias-header">
-    <h2 id="page-content-title" translate="Title_HelpDesk">Help Desk</h2>
+    <h2 id="page-content-title" translate="Title_Helpdesk">Help Desk</h2>
     <ias-search-box ng-model="$ctrl.query" ng-model-options="{debounce: $ctrl.inputDebounce}"
     <ias-search-box ng-model="$ctrl.query" ng-model-options="{debounce: $ctrl.inputDebounce}"
                     placeholder="{{'Placeholder_Search' | translate}}">
                     placeholder="{{'Placeholder_Search' | translate}}">
     </ias-search-box>
     </ias-search-box>

+ 3 - 3
client/src/modules/helpdesk/helpdesk-search-table.component.ts

@@ -54,7 +54,7 @@ export default class HelpDeskSearchTableComponent extends HelpDeskSearchBaseComp
     ];
     ];
     constructor($q: IQService,
     constructor($q: IQService,
                 $scope: IScope,
                 $scope: IScope,
-                private $state: angular.ui.IStateService,
+                $state: angular.ui.IStateService,
                 $stateParams: angular.ui.IStateParamsService,
                 $stateParams: angular.ui.IStateParamsService,
                 $timeout: ITimeoutService,
                 $timeout: ITimeoutService,
                 $translate: angular.translate.ITranslateService,
                 $translate: angular.translate.ITranslateService,
@@ -64,7 +64,7 @@ export default class HelpDeskSearchTableComponent extends HelpDeskSearchBaseComp
                 localStorageService: LocalStorageService,
                 localStorageService: LocalStorageService,
                 promiseService: PromiseService,
                 promiseService: PromiseService,
                 pwmService: IPwmService) {
                 pwmService: IPwmService) {
-        super($q, $scope, $stateParams, $timeout, $translate, configService, helpDeskService, IasDialogService,
+        super($q, $scope, $state, $stateParams, $timeout, $translate, configService, helpDeskService, IasDialogService,
               localStorageService, promiseService, pwmService);
               localStorageService, promiseService, pwmService);
     }
     }
 
 
@@ -99,7 +99,7 @@ export default class HelpDeskSearchTableComponent extends HelpDeskSearchBaseComp
     }
     }
 
 
     gotoCardsView(): void {
     gotoCardsView(): void {
-        this.$state.go('search.cards', {query: this.query});
+        this.toggleView('search.cards');
     }
     }
 
 
     toggleColumnVisible(event, columnId): void {
     toggleColumnVisible(event, columnId): void {

+ 1 - 0
client/src/modules/peoplesearch/person-details-dialog.component.scss

@@ -28,6 +28,7 @@
 
 
 .person-details-dialog {
 .person-details-dialog {
   text-align: left;
   text-align: left;
+  overflow: hidden;
 
 
   .ias-dialog-container {
   .ias-dialog-container {
     padding: 0;
     padding: 0;

+ 1 - 1
client/src/services/helpdesk-config.service.test-data.ts

@@ -1,4 +1,3 @@
-
 /*
 /*
  * Password Management Servlets (PWM)
  * Password Management Servlets (PWM)
  * http://www.pwm-project.org
  * http://www.pwm-project.org
@@ -20,6 +19,7 @@
  * along with this program; if not, write to the Free Software
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
  */
+
 /* tslint:disable */
 /* tslint:disable */
 
 
 export const helpdeskProcessAction_clientData = {
 export const helpdeskProcessAction_clientData = {

+ 1 - 0
client/src/services/helpdesk.service.test-data.ts

@@ -19,6 +19,7 @@
  * along with this program; if not, write to the Free Software
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
  */
+
 /* tslint:disable */
 /* tslint:disable */
 
 
 export const getRecentVerifications_response = {
 export const getRecentVerifications_response = {

+ 1 - 0
client/src/services/local-storage.service.ts

@@ -28,6 +28,7 @@ const KEYS = {
     SEARCH_TEXT: 'searchText',
     SEARCH_TEXT: 'searchText',
     HELPDESK_SEARCH_TEXT: 'helpdeskSearchText',
     HELPDESK_SEARCH_TEXT: 'helpdeskSearchText',
     SEARCH_VIEW: 'searchView',
     SEARCH_VIEW: 'searchView',
+    HELPDESK_SEARCH_VIEW: 'helpdeskSearchView',
     VERIFICATION_STATE: 'verificationState'
     VERIFICATION_STATE: 'verificationState'
 };
 };
 
 

+ 1 - 2
client/src/services/object.service.ts

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  * http://www.pwm-project.org
  *
  *
  * Copyright (c) 2006-2009 Novell, Inc.
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2016 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * it under the terms of the GNU General Public License as published by
@@ -20,7 +20,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
  */
 
 
-
 export default class ObjectService {
 export default class ObjectService {
     // ES5 implementation of Object.assign
     // ES5 implementation of Object.assign
     // Source from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
     // Source from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

+ 1 - 2
client/src/services/people.service.dev.ts

@@ -8,7 +8,7 @@
  * This program is free software; you can redistribute it and/or modify
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later versionI.
+ * (at your option) any later version.
  *
  *
  * This program is distributed in the hope that it will be useful,
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -20,7 +20,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
  */
 
 
-
 import { IPromise, IQService, ITimeoutService } from 'angular';
 import { IPromise, IQService, ITimeoutService } from 'angular';
 import { IPerson } from '../models/person.model';
 import { IPerson } from '../models/person.model';
 import {IPeopleService, IQuery} from './people.service';
 import {IPeopleService, IQuery} from './people.service';

+ 1 - 2
client/src/services/promise.service.ts

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  * http://www.pwm-project.org
  *
  *
  * Copyright (c) 2006-2009 Novell, Inc.
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2016 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * it under the terms of the GNU General Public License as published by
@@ -20,7 +20,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
  */
 
 
-
 import { IPromise, IQService } from 'angular';
 import { IPromise, IQService } from 'angular';
 
 
 // Pattern explained at https://www.bennadel.com/blog/2731-canceling-a-promise-in-angularjs.htm
 // Pattern explained at https://www.bennadel.com/blog/2731-canceling-a-promise-in-angularjs.htm

+ 0 - 2
data-service/README.md

@@ -1,2 +0,0 @@
-# pwm-data-service
-Cloud service for PWM

+ 0 - 325
data-service/checkstyle.xml

@@ -1,325 +0,0 @@
-<?xml version="1.0"?>
-<!--
-  ~ Password Management Servlets (PWM)
-  ~ http://www.pwm-project.org
-  ~
-  ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2016 The PWM Project
-  ~
-  ~ This program is free software; you can redistribute it and/or modify
-  ~ it under the terms of the GNU General Public License as published by
-  ~ the Free Software Foundation; either version 2 of the License, or
-  ~ (at your option) any later version.
-  ~
-  ~ This program is distributed in the hope that it will be useful,
-  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
-  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  ~ GNU General Public License for more details.
-  ~
-  ~ You should have received a copy of the GNU General Public License
-  ~ along with this program; if not, write to the Free Software
-  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-  -->
-
-<!DOCTYPE module PUBLIC
-        "-//Puppy Crawl//DTD Check Configuration 1.2//EN"
-        "http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
-
-<!--
-  PWM Checkstyle definition
--->
-
-<module name="Checker">
-
-    <!-- Checks that each Java package has a Javadoc file used for commenting. -->
-    <!-- See http://checkstyle.sf.net/config_javadoc.html#JavadocPackage       -->
-    <!--module name="JavadocPackage">
-      <property name="allowLegacy" value="true"/>
-    </module-->
-
-    <module name="FileLength"/>
-
-    <!-- Checks for Headers                              -->
-    <!-- See http://checkstyle.sf.net/config_header.html -->
-    <!--
-    <module name="RegexpHeader">
-        <property name="fileExtensions" value="java"/>
-        <property name="headerFile" value="${checkstyle.header.file}"/>
-    </module>
-    -->
-
-    <module name="FileTabCharacter">
-        <property name="eachLine" value="true"/>
-    </module>
-    <module name="NewlineAtEndOfFile"/>
-
-    <module name="TreeWalker">
-
-        <property name="cacheFile" value="target/checkstyle.cache"/>
-
-        <!-- required for SuppressWarningsFilter (and other Suppress* rules not used here) -->
-        <!-- see http://checkstyle.sourceforge.net/config_annotation.html#SuppressWarningsHolder -->
-        <module name="SuppressWarningsHolder"/>
-
-        <module name="OuterTypeFilename"/>
-        <module name="IllegalTokenText">
-            <property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
-            <property name="format" value="\\u00(08|09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
-            <property name="message" value="Avoid using corresponding octal or Unicode escape."/>
-        </module>
-        <module name="AvoidEscapedUnicodeCharacters">
-            <property name="allowEscapesForControlCharacters" value="true"/>
-            <property name="allowByTailComment" value="true"/>
-            <property name="allowNonPrintableEscapes" value="true"/>
-        </module>
-
-        <!--
-        <module name="LineLength">
-            <property name="max" value="200" />
-            <property name="ignorePattern" value="@version|@see|@todo|TODO"/>
-        </module>
-        -->
-        <!-- required for SuppressionCommentFilter -->
-        <!-- see http://checkstyle.sourceforge.net/config.html#SuppressionCommentFilter -->
-        <!--
-        <module name="FileContentsHolder"/>
-
-
-        -->
-
-        <module name="EmptyBlock">
-            <property name="option" value="TEXT"/>
-            <property name="tokens" value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
-        </module>
-        <!--
-        <module name="LeftCurly">
-            <property name="option" value="nl"/>
-            <property name="maxLineLength" value="100"/>
-        </module>
-        -->
-
-        <module name="RightCurly"/>
-        <module name="RightCurly">
-            <property name="option" value="alone"/>
-            <property name="tokens" value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT, INSTANCE_INIT"/>
-        </module>
-
-        <!--
-        <module name="MemberName" />
-        -->
-
-        <!-- Checks for Javadoc comments.                     -->
-        <!-- See http://checkstyle.sf.net/config_javadoc.html -->
-        <!--
-        <module name="JavadocMethod">
-            <property name="severity" value="warning"/>
-            <property name="scope" value="protected"/>
-        </module>
-        <module name="JavadocType">
-            <property name="scope" value="protected"/>
-            <property name="allowUnknownTags" value="true" />
-        </module>
-        <module name="JavadocVariable">
-            <property name="severity" value="info"/>
-            <property name="scope" value="protected"/>
-        </module>
-        -->
-
-        <module name="AnnotationLocation">
-            <property name="tokens" value="VARIABLE_DEF"/>
-            <property name="allowSamelineMultipleAnnotations" value="true"/>
-        </module>
-
-        <!-- Checks for Naming Conventions.                  -->
-        <!-- See http://checkstyle.sf.net/config_naming.html -->
-        <!--
-        <module name="MemberName">
-            <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
-        </module>
-        <module name="TypeName">
-        -->
-        <module name="ConstantName"/>
-        <module name="PackageName">
-            <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
-        </module>
-        <module name="LocalVariableName">
-            <property name="tokens" value="VARIABLE_DEF"/>
-            <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
-            <property name="allowOneCharVarInForLoop" value="true"/>
-        </module>
-        <!--
-        <module name="ClassTypeParameterName">
-            <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
-        </module>
-        -->
-        <module name="MethodTypeParameterName">
-            <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
-        </module>
-        <module name="InterfaceTypeParameterName">
-            <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
-        </module>
-        <!--
-        <module name="LocalFinalVariableName"/>
-        <module name="LocalVariableName"/>
-        <module name="MethodName"/>
-        <module name="PackageName"/>
-        <module name="ParameterName"/>
-        <module name="StaticVariableName"/>
-        <module name="TypeName"/>
-        -->
-
-        <!-- Checks for imports                              -->
-        <!-- See http://checkstyle.sf.net/config_import.html -->
-        <module name="AvoidStarImport"/>
-        <module name="AvoidStaticImport"/>
-        <module name="IllegalImport"/>
-        <module name="RedundantImport"/>
-        <module name="UnusedImports"/>
-
-
-        <!-- Checks for Size Violations.                    -->
-        <!-- See http://checkstyle.sf.net/config_sizes.html -->
-        <!--
-        <module name="MethodLength"/>
-        <module name="ParameterNumber"/>
-        -->
-
-
-        <!-- Checks for whitespace                               -->
-        <!-- See http://checkstyle.sf.net/config_whitespace.html -->
-        <module name="EmptyForIteratorPad">
-            <property name="option" value="space"/>
-        </module>
-        <module name="EmptyForInitializerPad"/>
-        <module name="NeedBraces"/>
-        <!--
-        -->
-        <!-- module name="NoWhitespaceAfter"/ -->
-        <!-- module name="NoWhitespaceBefore"/ -->
-        <!--
-        <module name="OperatorWrap"/>
-        <module name="ParenPad">
-            <property name="option" value="space" />
-        </module>
-        <module name="WhitespaceAfter"/>
-        <module name="WhitespaceAround"/>
-        -->
-        <!-- module name="MethodParamPad"/ -->
-        <module name="GenericWhitespace"/>
-        <module name="EmptyLineSeparator">
-            <property name="allowNoEmptyLineBetweenFields" value="true"/>
-        </module>
-
-
-
-        <!-- Modifier Checks                                    -->
-        <!-- See http://checkstyle.sf.net/config_modifiers.html -->
-        <module name="ModifierOrder"/>
-        <module name="RedundantModifier"/>
-        <!--
-        -->
-
-
-        <!-- Checks for blocks. You know, those {}'s         -->
-        <!-- See http://checkstyle.sf.net/config_blocks.html -->
-        <!--
-        <module name="AvoidNestedBlocks"/>
-        -->
-
-
-        <!-- Checks for common coding problems               -->
-        <!-- See http://checkstyle.sf.net/config_coding.html -->
-        <!-- module name="AvoidInlineConditionals"/ -->
-        <!--
-        <module name="EmptyStatement"/>
-        <module name="EqualsHashCode"/>
-        <module name="HiddenField">
-            <property name="severity" value="warning"/>
-            <property name="ignoreSetter" value="true"/>
-            <property name="ignoreConstructorParameter" value="true"/>
-        </module>
-        <module name="IllegalInstantiation"/>
-        <module name="InnerAssignment"/>
-        -->
-        <!--
-        <module name="MagicNumber">
-            <property name="ignoreNumbers" value="-4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 31, 32, 37, 64, 100, 128, 256, 512, 1000, 1024"/>
-        </module>
-        -->
-
-        <!-- Checks for class design                         -->
-        <!-- See http://checkstyle.sf.net/config_design.html -->
-        <!-- module name="DesignForExtension"/ -->
-        <!-- module name="FinalClass"/ -->
-        <!-- module name="HideUtilityClassConstructor"/ -->
-        <!--
-        <module name="InterfaceIsType"/>
-        <module name="VisibilityModifier">
-            <property name="protectedAllowed" value="true"/>
-            <property name="packageAllowed" value="true"/>
-        </module>
-        -->
-
-
-        <!-- future enabled checks -->
-        <!--
-        <module name="TrailingComment"/>
-        <module name="NPathComplexity"/>
-        <module name="EnumTrailingCommaCheck"/> //doesnt yet exist as of checkstyle 2.17
-        <module name="MultipleStringLiterals"/>
-        <module name="InnerAssignment"/>
-        <module name="MagicNumber">
-            <property name="ignoreNumbers" value="-4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 31, 32, 37, 64, 100, 128, 256, 512, 1000, 1024"/>
-        </module>
-        <module name="SimplifyBooleanExpression"/>
-        -->
-
-        <!-- coding -->
-        <module name="FallThrough"/>
-        <module name="EqualsHashCode"/>
-        <module name="ArrayTrailingCommaCheck"/>
-        <module name="FinalLocalVariable"/>
-        <module name="MissingSwitchDefault"/>
-        <module name="ModifiedControlVariable"/>
-        <module name="MultipleVariableDeclarations"/>
-        <module name="OneStatementPerLine"/>
-        <module name="FinalParameters"/>
-        <module name="ParameterAssignment"/>
-        <module name="SimplifyBooleanReturn"/>
-        <module name="StringLiteralEquality"/>
-        <module name="CovariantEquals"/>
-        <module name="DefaultComesLast"/>
-        <module name="EmptyStatement"/>
-        <module name="EqualsHashCode"/>
-        <module name="EqualsAvoidNull"/>
-
-        <module name="MutableException"/>
-        <module name="TodoComment"/>
-        <module name="NoLineWrap"/>
-        <module name="OneTopLevelClass"/>
-        <module name="NoFinalizer"/>
-        <module name="ArrayTypeStyle"/>
-        <module name="UpperEll"/>
-        <module name="PackageDeclaration"/>
-        <module name="NoClone"/>
-    </module>
-
-    <!-- Support @SuppressWarnings (added in Checkstyle 5.7) -->
-    <!-- see http://checkstyle.sourceforge.net/config.html#SuppressWarningsFilter -->
-    <module name="SuppressWarningsFilter"/>
-
-    <!-- Checks properties file for a duplicated properties. -->
-    <!-- See http://checkstyle.sourceforge.net/config_misc.html#UniqueProperties -->
-    <module name="UniqueProperties"/>
-
-    <!-- Support CHECKSTYLE_OFF: regexp and CHECKSTYLE_ON: regexp comments to disable/enable some checks -->
-    <!-- see http://checkstyle.sourceforge.net/config.html#SuppressionCommentFilter -->
-    <!--
-    <module name="SuppressionCommentFilter">
-        <property name="offCommentFormat" value="CHECKSTYLE_OFF\: (.+)"/>
-        <property name="onCommentFormat" value="CHECKSTYLE_ON\: (.+)"/>
-        <property name="checkFormat" value="$1"/>
-    </module>
-    -->
-
-</module>

+ 9 - 158
data-service/pom.xml

@@ -29,63 +29,18 @@
     </organization>
     </organization>
 
 
     <properties>
     <properties>
-        <maven.compiler.source>1.8</maven.compiler.source>
-        <maven.compiler.target>1.8</maven.compiler.target>
         <skipTests>false</skipTests>
         <skipTests>false</skipTests>
         <timestamp.iso>${maven.build.timestamp}</timestamp.iso>
         <timestamp.iso>${maven.build.timestamp}</timestamp.iso>
         <maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss'Z'</maven.build.timestamp.format>
         <maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss'Z'</maven.build.timestamp.format>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-        <build.number>0</build.number>  <!-- default in case not set on command line -->
-        <build.revision>0</build.revision>  <!-- default in case not set on command line -->
+        <project.root.basedir>${project.basedir}/..</project.root.basedir>
     </properties>
     </properties>
 
 
     <profiles>
     <profiles>
-        <profile>
-            <id>skip-all</id>
-            <properties>
-                <maven.javadoc.skip>true</maven.javadoc.skip>
-                <source.skip>true</source.skip>
-                <skipTests>true</skipTests>
-                <checkstyle.skip>true</checkstyle.skip>
-                <skip.npm>true</skip.npm>
-            </properties>
-        </profile>
-        <profile>
-            <id>skip-tests</id>
-            <properties>
-                <skipTests>true</skipTests>
-            </properties>
-        </profile>
-        <profile>
-            <id>skip-checkstyle</id>
-            <properties>
-                <checkstyle.skip>true</checkstyle.skip>
-            </properties>
-        </profile>
     </profiles>
     </profiles>
 
 
     <build>
     <build>
         <plugins>
         <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-enforcer-plugin</artifactId>
-                <version>3.0.0-M2</version>
-                <executions>
-                    <execution>
-                        <id>enforce-maven</id>
-                        <goals>
-                            <goal>enforce</goal>
-                        </goals>
-                        <configuration>
-                            <rules>
-                                <requireMavenVersion>
-                                    <version>3.3</version>
-                                </requireMavenVersion>
-                            </rules>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
             <plugin>
             <plugin>
                 <!-- This plugin will set properties values using dependency information -->
                 <!-- This plugin will set properties values using dependency information -->
                 <groupId>org.apache.maven.plugins</groupId>
                 <groupId>org.apache.maven.plugins</groupId>
@@ -98,57 +53,10 @@
                     </execution>
                     </execution>
                 </executions>
                 </executions>
             </plugin>
             </plugin>
-
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-compiler-plugin</artifactId>
-                <version>3.3</version>
-                <configuration>
-                    <source>${maven.compiler.source}</source>
-                    <target>${maven.compiler.target}</target>
-
-                </configuration>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-surefire-plugin</artifactId>
-                <version>2.19.1</version>
-                <configuration>
-                    <excludes>
-                        <exclude>password.pwm.manual.*</exclude>
-                    </excludes>
-                </configuration>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-source-plugin</artifactId>
-                <version>2.4</version>
-                <executions>
-                    <execution>
-                        <id>attach-sources</id>
-                        <goals>
-                            <goal>jar-no-fork</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-javadoc-plugin</artifactId>
-                <version>2.10.3</version>
-                <executions>
-                    <execution>
-                        <id>attach-javadocs</id>
-                        <goals>
-                            <goal>jar</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
             <plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-war-plugin</artifactId>
                 <artifactId>maven-war-plugin</artifactId>
-                <version>2.6</version>
+                <version>3.2.2</version>
                 <configuration>
                 <configuration>
                     <archiveClasses>true</archiveClasses>
                     <archiveClasses>true</archiveClasses>
                     <packagingExcludes>WEB-INF/classes</packagingExcludes>
                     <packagingExcludes>WEB-INF/classes</packagingExcludes>
@@ -165,37 +73,9 @@
                     </archive>
                     </archive>
                 </configuration>
                 </configuration>
             </plugin>
             </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-checkstyle-plugin</artifactId>
-                <version>3.0.0</version>
-                <dependencies>
-                    <dependency>
-                        <groupId>com.puppycrawl.tools</groupId>
-                        <artifactId>checkstyle</artifactId>
-                        <version>8.10.1</version>
-                    </dependency>
-                </dependencies>
-                <executions>
-                    <execution>
-                        <id>validate</id>
-                        <phase>validate</phase>
-                        <configuration>
-                            <encoding>UTF-8</encoding>
-                            <consoleOutput>true</consoleOutput>
-                            <includeTestResources>false</includeTestResources>
-                            <failsOnError>true</failsOnError>
-                            <configLocation>checkstyle.xml</configLocation>
-                        </configuration>
-                        <goals>
-                            <goal>check</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
             <plugin>
             <plugin>
                 <artifactId>maven-resources-plugin</artifactId>
                 <artifactId>maven-resources-plugin</artifactId>
-                <version>2.7</version>
+                <version>3.1.0</version>
                 <executions>
                 <executions>
                     <execution>
                     <execution>
                         <id>copy-resources</id>
                         <id>copy-resources</id>
@@ -215,7 +95,7 @@
             </plugin>
             </plugin>
             <plugin>
             <plugin>
                 <artifactId>maven-clean-plugin</artifactId>
                 <artifactId>maven-clean-plugin</artifactId>
-                <version>3.0.0</version>
+                <version>3.1.0</version>
             </plugin>
             </plugin>
         </plugins>
         </plugins>
     </build>
     </build>
@@ -226,18 +106,11 @@
     <dependencies>
     <dependencies>
         <dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <groupId>${project.groupId}</groupId>
-            <artifactId>pwm</artifactId>
+            <artifactId>pwm-server</artifactId>
             <version>${project.version}</version>
             <version>${project.version}</version>
             <type>jar</type>
             <type>jar</type>
         </dependency>
         </dependency>
 
 
-        <!-- dev tool -->
-        <dependency>
-            <groupId>org.projectlombok</groupId>
-            <artifactId>lombok</artifactId>
-            <version>1.18.0</version>
-            <scope>provided</scope>
-        </dependency>
 
 
         <!-- container dependencies -->
         <!-- container dependencies -->
         <dependency>
         <dependency>
@@ -262,22 +135,22 @@
         <dependency>
         <dependency>
             <groupId>org.apache.commons</groupId>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-csv</artifactId>
             <artifactId>commons-csv</artifactId>
-            <version>1.5</version>
+            <version>1.6</version>
         </dependency>
         </dependency>
         <dependency>
         <dependency>
             <groupId>org.apache.commons</groupId>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-lang3</artifactId>
             <artifactId>commons-lang3</artifactId>
-            <version>3.7</version>
+            <version>3.8.1</version>
         </dependency>
         </dependency>
         <dependency>
         <dependency>
             <groupId>com.sun.mail</groupId>
             <groupId>com.sun.mail</groupId>
             <artifactId>javax.mail</artifactId>
             <artifactId>javax.mail</artifactId>
-            <version>1.6.1</version>
+            <version>1.6.2</version>
         </dependency>
         </dependency>
         <dependency>
         <dependency>
             <groupId>org.apache.httpcomponents</groupId>
             <groupId>org.apache.httpcomponents</groupId>
             <artifactId>httpclient</artifactId>
             <artifactId>httpclient</artifactId>
-            <version>4.5.5</version>
+            <version>4.5.6</version>
         </dependency>
         </dependency>
         <dependency>
         <dependency>
             <groupId>log4j</groupId>
             <groupId>log4j</groupId>
@@ -309,27 +182,5 @@
             <artifactId>webjars-locator-core</artifactId>
             <artifactId>webjars-locator-core</artifactId>
             <version>0.35</version>
             <version>0.35</version>
         </dependency>
         </dependency>
-
-
-
     </dependencies>
     </dependencies>
-
-    <repositories>
-        <repository>
-            <id>central</id>
-            <url>https://repo1.maven.org/maven2</url>
-            <snapshots>
-                <enabled>false</enabled>
-            </snapshots>
-        </repository>
-    </repositories>
-    <pluginRepositories>
-        <pluginRepository>
-            <id>central</id>
-            <url>https://repo1.maven.org/maven2</url>
-            <snapshots>
-                <enabled>false</enabled>
-            </snapshots>
-        </pluginRepository>
-    </pluginRepositories>
 </project>
 </project>

+ 13 - 9
data-service/src/main/java/password/pwm/receiver/ContextManager.java

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  * http://www.pwm-project.org
  *
  *
  * Copyright (c) 2006-2009 Novell, Inc.
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@
  * You should have received a copy of the GNU General Public License
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
  */
  */
 
 
 package password.pwm.receiver;
 package password.pwm.receiver;
@@ -29,27 +28,32 @@ import javax.servlet.ServletContextListener;
 import javax.servlet.annotation.WebListener;
 import javax.servlet.annotation.WebListener;
 
 
 @WebListener
 @WebListener
-public class ContextManager implements ServletContextListener {
+public class ContextManager implements ServletContextListener
+{
     private static final String CONTEXT_ATTR = "contextManager";
     private static final String CONTEXT_ATTR = "contextManager";
     private PwmReceiverApp app;
     private PwmReceiverApp app;
 
 
     @Override
     @Override
-    public void contextInitialized(final ServletContextEvent sce) {
+    public void contextInitialized( final ServletContextEvent sce )
+    {
         app = new PwmReceiverApp();
         app = new PwmReceiverApp();
-        sce.getServletContext().setAttribute(CONTEXT_ATTR, this);
+        sce.getServletContext().setAttribute( CONTEXT_ATTR, this );
     }
     }
 
 
     @Override
     @Override
-    public void contextDestroyed(final ServletContextEvent sce) {
+    public void contextDestroyed( final ServletContextEvent sce )
+    {
         app.close();
         app.close();
         app = null;
         app = null;
     }
     }
 
 
-    public PwmReceiverApp getApp() {
+    public PwmReceiverApp getApp( )
+    {
         return app;
         return app;
     }
     }
 
 
-    public static ContextManager getContextManager(final ServletContext serverContext) {
-        return (ContextManager)serverContext.getAttribute(CONTEXT_ATTR);
+    public static ContextManager getContextManager( final ServletContext serverContext )
+    {
+        return ( ContextManager ) serverContext.getAttribute( CONTEXT_ATTR );
     }
     }
 }
 }

+ 5 - 5
data-service/src/main/java/password/pwm/receiver/CsvDownloadServlet.java

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  * http://www.pwm-project.org
  *
  *
  * Copyright (c) 2006-2009 Novell, Inc.
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@
  * You should have received a copy of the GNU General Public License
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
  */
  */
 
 
 package password.pwm.receiver;
 package password.pwm.receiver;
@@ -26,10 +25,11 @@ package password.pwm.receiver;
 import javax.servlet.annotation.WebServlet;
 import javax.servlet.annotation.WebServlet;
 
 
 @WebServlet(
 @WebServlet(
-        name="TelemetryViewer",
-        urlPatterns={
+        name = "TelemetryViewer",
+        urlPatterns = {
                 "/csv",
                 "/csv",
         }
         }
 )
 )
-public class CsvDownloadServlet {
+public class CsvDownloadServlet
+{
 }
 }

+ 81 - 57
data-service/src/main/java/password/pwm/receiver/FtpDataIngestor.java

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  * http://www.pwm-project.org
  *
  *
  * Copyright (c) 2006-2009 Novell, Inc.
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@
  * You should have received a copy of the GNU General Public License
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
  */
  */
 
 
 package password.pwm.receiver;
 package password.pwm.receiver;
@@ -42,102 +41,126 @@ import java.util.List;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 import java.util.zip.ZipInputStream;
 
 
-class FtpDataIngestor {
+class FtpDataIngestor
+{
 
 
-    private static final PwmReceiverLogger LOGGER = PwmReceiverLogger.forClass(FtpDataIngestor.class);
+    private static final PwmReceiverLogger LOGGER = PwmReceiverLogger.forClass( FtpDataIngestor.class );
 
 
     private final Settings settings;
     private final Settings settings;
     private final PwmReceiverApp app;
     private final PwmReceiverApp app;
 
 
-    FtpDataIngestor(final PwmReceiverApp app, final Settings telemetrySettings) {
+    FtpDataIngestor( final PwmReceiverApp app, final Settings telemetrySettings )
+    {
         this.app = app;
         this.app = app;
         this.settings = telemetrySettings;
         this.settings = telemetrySettings;
     }
     }
 
 
-    void readData(final Storage storage) {
-        app.getStatus().setLastFtpStatus("beginning ftp ingestion");
+    void readData( final Storage storage )
+    {
+        app.getStatus().setLastFtpStatus( "beginning ftp ingestion" );
         LOGGER.debug( "beginning ftp ingestion" );
         LOGGER.debug( "beginning ftp ingestion" );
-        app.getStatus().setLastFtpIngest(Instant.now());
-        try {
+        app.getStatus().setLastFtpIngest( Instant.now() );
+        try
+        {
             final FTPClient ftpClient = getFtpClient();
             final FTPClient ftpClient = getFtpClient();
-            final List<String> files = getFiles(ftpClient);
+            final List<String> files = getFiles( ftpClient );
             LOGGER.debug( "beginning ftp ingestion, listed " + files.size() + " files from server" );
             LOGGER.debug( "beginning ftp ingestion, listed " + files.size() + " files from server" );
-            for (final String fileName : files) {
-                if (fileName != null && fileName.endsWith(".zip")) {
-                    app.getStatus().setLastFtpIngest(Instant.now());
-                    app.getStatus().setLastFtpStatus("reading file " + fileName);
+            for ( final String fileName : files )
+            {
+                if ( fileName != null && fileName.endsWith( ".zip" ) )
+                {
+                    app.getStatus().setLastFtpIngest( Instant.now() );
+                    app.getStatus().setLastFtpStatus( "reading file " + fileName );
                     LOGGER.debug( "read file " + fileName );
                     LOGGER.debug( "read file " + fileName );
-                    try {
+                    try
+                    {
                         readFile( ftpClient, fileName, storage );
                         readFile( ftpClient, fileName, storage );
-                    } catch (Exception e) {
-                        app.getStatus().setLastFtpIngest(Instant.now());
+                    }
+                    catch ( Exception e )
+                    {
+                        app.getStatus().setLastFtpIngest( Instant.now() );
                         final String msg = "error while reading ftp file '" + fileName + "': " + e.getMessage();
                         final String msg = "error while reading ftp file '" + fileName + "': " + e.getMessage();
-                        app.getStatus().setLastFtpStatus(msg);
+                        app.getStatus().setLastFtpStatus( msg );
                         LOGGER.error( msg );
                         LOGGER.error( msg );
                     }
                     }
-                } else {
-                    LOGGER.info("skipping ftp file " + fileName);
+                }
+                else
+                {
+                    LOGGER.info( "skipping ftp file " + fileName );
                 }
                 }
             }
             }
             ftpClient.disconnect();
             ftpClient.disconnect();
-            LOGGER.info("completed ftp ingestion");
-            app.getStatus().setLastFtpStatus("completed successfully");
-            app.getStatus().setLastFtpIngest(Instant.now());
+            LOGGER.info( "completed ftp ingestion" );
+            app.getStatus().setLastFtpStatus( "completed successfully" );
+            app.getStatus().setLastFtpIngest( Instant.now() );
             app.getStatus().setLastFtpFilesRead( files.size() );
             app.getStatus().setLastFtpFilesRead( files.size() );
-        } catch (Exception e) {
-            app.getStatus().setLastFtpIngest(Instant.now());
-            app.getStatus().setLastFtpStatus("error during ftp scan: " + e.getMessage());
+        }
+        catch ( Exception e )
+        {
+            app.getStatus().setLastFtpIngest( Instant.now() );
+            app.getStatus().setLastFtpStatus( "error during ftp scan: " + e.getMessage() );
         }
         }
     }
     }
 
 
-    private void readFile(final FTPClient ftpClient, final String fileName, final Storage storage) throws Exception {
+    private void readFile( final FTPClient ftpClient, final String fileName, final Storage storage ) throws Exception
+    {
         final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
         final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-        ftpClient.retrieveFile(fileName, byteArrayOutputStream);
-        final ByteArrayInputStream inputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
-        readZippedByteStream(inputStream, fileName, storage);
+        ftpClient.retrieveFile( fileName, byteArrayOutputStream );
+        final ByteArrayInputStream inputStream = new ByteArrayInputStream( byteArrayOutputStream.toByteArray() );
+        readZippedByteStream( inputStream, fileName, storage );
     }
     }
 
 
-    private void readZippedByteStream(final InputStream inputStream, final String fileName, final Storage storage) throws Exception {
-        try {
-            final ZipInputStream zipInputStream = new ZipInputStream(inputStream);
+    private void readZippedByteStream( final InputStream inputStream, final String fileName, final Storage storage ) throws Exception
+    {
+        try
+        {
+            final ZipInputStream zipInputStream = new ZipInputStream( inputStream );
             final ZipEntry zipEntry = zipInputStream.getNextEntry();
             final ZipEntry zipEntry = zipInputStream.getNextEntry();
             final String zipEntryName = zipEntry.getName();
             final String zipEntryName = zipEntry.getName();
-            if (zipEntryName != null && zipEntryName.endsWith(".json")) {
-                LOGGER.info("reading ftp file " + fileName + ":" + zipEntryName);
+            if ( zipEntryName != null && zipEntryName.endsWith( ".json" ) )
+            {
+                LOGGER.info( "reading ftp file " + fileName + ":" + zipEntryName );
                 final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                 final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-                final byte[] buffer = new byte[1024];
+                final byte[] buffer = new byte[ 1024 ];
                 int len;
                 int len;
-                while ((len = zipInputStream.read(buffer)) > 0) {
-                    byteArrayOutputStream.write(buffer, 0, len);
+                while ( ( len = zipInputStream.read( buffer ) ) > 0 )
+                {
+                    byteArrayOutputStream.write( buffer, 0, len );
                 }
                 }
-                final String resultsStr = byteArrayOutputStream.toString(PwmConstants.DEFAULT_CHARSET.name());
-                final TelemetryPublishBean bean = JsonUtil.deserialize(resultsStr, TelemetryPublishBean.class);
-                storage.store(bean);
+                final String resultsStr = byteArrayOutputStream.toString( PwmConstants.DEFAULT_CHARSET.name() );
+                final TelemetryPublishBean bean = JsonUtil.deserialize( resultsStr, TelemetryPublishBean.class );
+                storage.store( bean );
             }
             }
-        } catch (Exception e) {
+        }
+        catch ( Exception e )
+        {
             final String msg = "error reading ftp file '" + fileName + "', error: " + e.getMessage();
             final String msg = "error reading ftp file '" + fileName + "', error: " + e.getMessage();
-            LOGGER.info(msg);
-            throw new Exception(e);
+            LOGGER.info( msg );
+            throw new Exception( e );
         }
         }
     }
     }
 
 
-    private List<String> getFiles(final FTPClient ftpClient) throws IOException {
+    private List<String> getFiles( final FTPClient ftpClient ) throws IOException
+    {
         final String pathname = settings.getSetting( Settings.Setting.ftpReadPath );
         final String pathname = settings.getSetting( Settings.Setting.ftpReadPath );
-        final FTPFile[] files = ftpClient.listFiles(pathname);
+        final FTPFile[] files = ftpClient.listFiles( pathname );
         final List<String> returnFiles = new ArrayList<>();
         final List<String> returnFiles = new ArrayList<>();
-        for (final FTPFile ftpFile : files) {
+        for ( final FTPFile ftpFile : files )
+        {
             final String name = ftpFile.getName();
             final String name = ftpFile.getName();
             final String fullPath = pathname + "/" + name;
             final String fullPath = pathname + "/" + name;
-            returnFiles.add(fullPath);
+            returnFiles.add( fullPath );
         }
         }
 
 
-        return Collections.unmodifiableList(returnFiles);
+        return Collections.unmodifiableList( returnFiles );
     }
     }
 
 
-    private FTPClient getFtpClient() throws IOException {
+    private FTPClient getFtpClient( ) throws IOException
+    {
         final FTPClient ftpClient;
         final FTPClient ftpClient;
         final Settings.FtpMode ftpMode = Settings.FtpMode.valueOf( settings.getSetting( Settings.Setting.ftpMode ) );
         final Settings.FtpMode ftpMode = Settings.FtpMode.valueOf( settings.getSetting( Settings.Setting.ftpMode ) );
-        switch ( ftpMode ) {
+        switch ( ftpMode )
+        {
             case ftp:
             case ftp:
                 ftpClient = new FTPClient();
                 ftpClient = new FTPClient();
                 break;
                 break;
@@ -147,14 +170,15 @@ class FtpDataIngestor {
                 break;
                 break;
 
 
             default:
             default:
-                throw new IllegalArgumentException("unexpected ftp mode");
+                throw new IllegalArgumentException( "unexpected ftp mode" );
         }
         }
 
 
-        ftpClient.connect( settings.getSetting( Settings.Setting.ftpSite ));
-        LOGGER.info("ftp connect complete");
-        if (!StringUtil.isEmpty(settings.getSetting(Settings.Setting.ftpUser)) && !StringUtil.isEmpty(settings.getSetting( Settings.Setting.ftpPassword ))) {
-            final boolean loggedInSuccess = ftpClient.login( settings.getSetting(Settings.Setting.ftpUser), settings.getSetting( Settings.Setting.ftpPassword ));
-            LOGGER.info("ftp login complete, success=" + loggedInSuccess);
+        ftpClient.connect( settings.getSetting( Settings.Setting.ftpSite ) );
+        LOGGER.info( "ftp connect complete" );
+        if ( !StringUtil.isEmpty( settings.getSetting( Settings.Setting.ftpUser ) ) && !StringUtil.isEmpty( settings.getSetting( Settings.Setting.ftpPassword ) ) )
+        {
+            final boolean loggedInSuccess = ftpClient.login( settings.getSetting( Settings.Setting.ftpUser ), settings.getSetting( Settings.Setting.ftpPassword ) );
+            LOGGER.info( "ftp login complete, success=" + loggedInSuccess );
         }
         }
         ftpClient.enterLocalPassiveMode();
         ftpClient.enterLocalPassiveMode();
         return ftpClient;
         return ftpClient;

+ 11 - 8
data-service/src/main/java/password/pwm/receiver/Logger.java

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  * http://www.pwm-project.org
  *
  *
  * Copyright (c) 2006-2009 Novell, Inc.
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * it under the terms of the GNU General Public License as published by
@@ -18,24 +18,27 @@
  * You should have received a copy of the GNU General Public License
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
  */
  */
 
 
 package password.pwm.receiver;
 package password.pwm.receiver;
 
 
-public class Logger {
+public class Logger
+{
 
 
     private final String name;
     private final String name;
 
 
-    private Logger(final String name) {
+    private Logger( final String name )
+    {
         this.name = name;
         this.name = name;
     }
     }
 
 
-    public static Logger createLogger(final String name) {
-        return new Logger(name);
+    public static Logger createLogger( final String name )
+    {
+        return new Logger( name );
     }
     }
 
 
-    public void info(final CharSequence input) {
-        System.out.println(input);
+    public void info( final CharSequence input )
+    {
+        System.out.println( input );
     }
     }
 }
 }

+ 37 - 23
data-service/src/main/java/password/pwm/receiver/PwmReceiverApp.java

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  * http://www.pwm-project.org
  *
  *
  * Copyright (c) 2006-2009 Novell, Inc.
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@
  * You should have received a copy of the GNU General Public License
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
  */
  */
 
 
 package password.pwm.receiver;
 package password.pwm.receiver;
@@ -31,7 +30,8 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeUnit;
 
 
-public class PwmReceiverApp {
+public class PwmReceiverApp
+{
     private static final PwmReceiverLogger LOGGER = PwmReceiverLogger.forClass( PwmReceiverApp.class );
     private static final PwmReceiverLogger LOGGER = PwmReceiverLogger.forClass( PwmReceiverApp.class );
     private static final String ENV_NAME = "DATA_SERVICE_PROPS";
     private static final String ENV_NAME = "DATA_SERVICE_PROPS";
 
 
@@ -40,56 +40,70 @@ public class PwmReceiverApp {
     private Settings settings;
     private Settings settings;
     private Status status = new Status();
     private Status status = new Status();
 
 
-    public PwmReceiverApp() {
-        final String propsFile = System.getenv(ENV_NAME);
-        if (StringUtil.isEmpty(propsFile)) {
+    public PwmReceiverApp( )
+    {
+        final String propsFile = System.getenv( ENV_NAME );
+        if ( StringUtil.isEmpty( propsFile ) )
+        {
             final String errorMsg = "Missing environment variable '" + ENV_NAME + "', can't load configuration";
             final String errorMsg = "Missing environment variable '" + ENV_NAME + "', can't load configuration";
             status.setErrorState( errorMsg );
             status.setErrorState( errorMsg );
             LOGGER.error( errorMsg );
             LOGGER.error( errorMsg );
             return;
             return;
         }
         }
 
 
-        try {
-            settings = Settings.readFromFile(propsFile);
-        } catch (IOException e) {
-            final String errorMsg = "can't read configuration: " + JavaHelper.readHostileExceptionMessage(e);
+        try
+        {
+            settings = Settings.readFromFile( propsFile );
+        }
+        catch ( IOException e )
+        {
+            final String errorMsg = "can't read configuration: " + JavaHelper.readHostileExceptionMessage( e );
             status.setErrorState( errorMsg );
             status.setErrorState( errorMsg );
             LOGGER.error( errorMsg, e );
             LOGGER.error( errorMsg, e );
             return;
             return;
         }
         }
 
 
-        try {
-            storage = new Storage(settings);
-        } catch (Exception e) {
-            final String errorMsg = "can't start storage system: " + JavaHelper.readHostileExceptionMessage(e);
+        try
+        {
+            storage = new Storage( settings );
+        }
+        catch ( Exception e )
+        {
+            final String errorMsg = "can't start storage system: " + JavaHelper.readHostileExceptionMessage( e );
             status.setErrorState( errorMsg );
             status.setErrorState( errorMsg );
             LOGGER.error( errorMsg, e );
             LOGGER.error( errorMsg, e );
             return;
             return;
         }
         }
 
 
-        if (settings.getSetting( Settings.Setting.ftpSite ) != null && !settings.getSetting( Settings.Setting.ftpSite ).isEmpty()) {
-            final Runnable ftpThread = () -> {
-                final FtpDataIngestor ftpDataIngestor = new FtpDataIngestor(this, settings);
-                ftpDataIngestor.readData(storage);
+        if ( settings.getSetting( Settings.Setting.ftpSite ) != null && !settings.getSetting( Settings.Setting.ftpSite ).isEmpty() )
+        {
+            final Runnable ftpThread = ( ) ->
+            {
+                final FtpDataIngestor ftpDataIngestor = new FtpDataIngestor( this, settings );
+                ftpDataIngestor.readData( storage );
             };
             };
-            scheduledExecutorService.scheduleAtFixedRate(ftpThread, 0, 1, TimeUnit.HOURS);
+            scheduledExecutorService.scheduleAtFixedRate( ftpThread, 0, 1, TimeUnit.HOURS );
         }
         }
     }
     }
 
 
-    public Settings getSettings() {
+    public Settings getSettings( )
+    {
         return settings;
         return settings;
     }
     }
 
 
-    public Storage getStorage() {
+    public Storage getStorage( )
+    {
         return storage;
         return storage;
     }
     }
 
 
-    void close() {
+    void close( )
+    {
         storage.close();
         storage.close();
         scheduledExecutorService.shutdown();
         scheduledExecutorService.shutdown();
     }
     }
 
 
-    public Status getStatus() {
+    public Status getStatus( )
+    {
         return status;
         return status;
     }
     }
 
 

+ 33 - 6
data-service/src/main/java/password/pwm/receiver/PwmReceiverLogger.java

@@ -1,3 +1,25 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2018 The PWM Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
 package password.pwm.receiver;
 package password.pwm.receiver;
 
 
 import java.util.logging.Level;
 import java.util.logging.Level;
@@ -17,24 +39,29 @@ public class PwmReceiverLogger
         return new PwmReceiverLogger( clazz );
         return new PwmReceiverLogger( clazz );
     }
     }
 
 
-    public void debug(final CharSequence logMsg) {
+    public void debug( final CharSequence logMsg )
+    {
         log( Level.FINE, logMsg, null );
         log( Level.FINE, logMsg, null );
     }
     }
 
 
-    public void info(final CharSequence logMsg) {
+    public void info( final CharSequence logMsg )
+    {
         log( Level.INFO, logMsg, null );
         log( Level.INFO, logMsg, null );
     }
     }
 
 
-    public void error(final CharSequence logMsg ) {
+    public void error( final CharSequence logMsg )
+    {
         log( Level.SEVERE, logMsg, null );
         log( Level.SEVERE, logMsg, null );
     }
     }
 
 
-    public void error(final CharSequence logMsg, final Throwable throwable ) {
+    public void error( final CharSequence logMsg, final Throwable throwable )
+    {
         log( Level.SEVERE, logMsg, throwable );
         log( Level.SEVERE, logMsg, throwable );
     }
     }
 
 
-    private void log( final Level level, final CharSequence logMsg, final Throwable throwable ) {
-        final Logger logger = Logger.getLogger(clazz.getName());
+    private void log( final Level level, final CharSequence logMsg, final Throwable throwable )
+    {
+        final Logger logger = Logger.getLogger( clazz.getName() );
         logger.log( level, logMsg.toString(), throwable );
         logger.log( level, logMsg.toString(), throwable );
     }
     }
 }
 }

+ 33 - 24
data-service/src/main/java/password/pwm/receiver/Settings.java

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  * http://www.pwm-project.org
  *
  *
  * Copyright (c) 2006-2009 Novell, Inc.
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@
  * You should have received a copy of the GNU General Public License
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
  */
  */
 
 
 package password.pwm.receiver;
 package password.pwm.receiver;
@@ -26,25 +25,29 @@ package password.pwm.receiver;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
 
 
-import java.io.FileReader;
+import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Properties;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeUnit;
 
 
-public class Settings {
-    enum Setting {
-        ftpMode(FtpMode.ftp.name()),
-        ftpSite(null),
-        ftpUser(null),
-        ftpPassword(null),
-        ftpReadPath(null),
-        storagePath(null),
-        maxInstanceSeconds(Long.toString( new TimeDuration(14, TimeUnit.DAYS).getTotalSeconds() ) ),
-
-        ;
+public class Settings
+{
+    enum Setting
+    {
+        ftpMode( FtpMode.ftp.name() ),
+        ftpSite( null ),
+        ftpUser( null ),
+        ftpPassword( null ),
+        ftpReadPath( null ),
+        storagePath( null ),
+        maxInstanceSeconds( Long.toString( new TimeDuration( 14, TimeUnit.DAYS ).getTotalSeconds() ) ),;
 
 
         private final String defaultValue;
         private final String defaultValue;
 
 
@@ -59,28 +62,33 @@ public class Settings {
         }
         }
     }
     }
 
 
-    enum FtpMode {
+    enum FtpMode
+    {
         ftp,
         ftp,
         ftps,
         ftps,
     }
     }
 
 
-    private final Map<Setting,String> settings;
+    private final Map<Setting, String> settings;
 
 
     private Settings( final Map<Setting, String> settings )
     private Settings( final Map<Setting, String> settings )
     {
     {
         this.settings = settings;
         this.settings = settings;
     }
     }
 
 
-    static Settings readFromFile( final String filename) throws IOException {
+    static Settings readFromFile( final String filename ) throws IOException
+    {
         final Properties properties = new Properties();
         final Properties properties = new Properties();
-        properties.load(new FileReader(filename));
-        final Map<Setting,String> returnMap = new HashMap<>(  );
-        for (final Setting setting : Setting.values() )
+        try ( Reader reader = new InputStreamReader( new FileInputStream( new File( filename ) ), StandardCharsets.UTF_8 ) )
         {
         {
-            final String value = properties.getProperty( setting.name(), setting.getDefaultValue() );
-            returnMap.put( setting, value );
+            properties.load( reader );
+            final Map<Setting, String> returnMap = new HashMap<>();
+            for ( final Setting setting : Setting.values() )
+            {
+                final String value = properties.getProperty( setting.name(), setting.getDefaultValue() );
+                returnMap.put( setting, value );
+            }
+            return new Settings( Collections.unmodifiableMap( returnMap ) );
         }
         }
-        return new Settings( Collections.unmodifiableMap( returnMap ) );
     }
     }
 
 
     public String getSetting( final Setting setting )
     public String getSetting( final Setting setting )
@@ -88,7 +96,8 @@ public class Settings {
         return settings.get( setting );
         return settings.get( setting );
     }
     }
 
 
-    public boolean isFtpEnabled() {
+    public boolean isFtpEnabled( )
+    {
         final String value = settings.get( Setting.ftpSite );
         final String value = settings.get( Setting.ftpSite );
         return !StringUtil.isEmpty( value );
         return !StringUtil.isEmpty( value );
     }
     }

+ 3 - 3
data-service/src/main/java/password/pwm/receiver/Status.java

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  * http://www.pwm-project.org
  *
  *
  * Copyright (c) 2006-2009 Novell, Inc.
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@
  * You should have received a copy of the GNU General Public License
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
  */
  */
 
 
 package password.pwm.receiver;
 package password.pwm.receiver;
@@ -30,7 +29,8 @@ import java.time.Instant;
 
 
 @Getter
 @Getter
 @Setter
 @Setter
-public class Status {
+public class Status
+{
     private String errorState;
     private String errorState;
     private String lastFtpStatus;
     private String lastFtpStatus;
     private Instant lastFtpIngest;
     private Instant lastFtpIngest;

+ 95 - 56
data-service/src/main/java/password/pwm/receiver/Storage.java

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  * http://www.pwm-project.org
  *
  *
  * Copyright (c) 2006-2009 Novell, Inc.
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@
  * You should have received a copy of the GNU General Public License
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
  */
  */
 
 
 package password.pwm.receiver;
 package password.pwm.receiver;
@@ -42,123 +41,152 @@ import java.io.IOException;
 import java.time.Instant;
 import java.time.Instant;
 import java.util.Iterator;
 import java.util.Iterator;
 
 
-public class Storage {
+public class Storage
+{
     private final Environment environment;
     private final Environment environment;
     private Store store;
     private Store store;
 
 
-    public Storage(final Settings settings) throws IOException {
+    public Storage( final Settings settings ) throws IOException
+    {
         final String path = settings.getSetting( Settings.Setting.storagePath );
         final String path = settings.getSetting( Settings.Setting.storagePath );
-        if (path == null) {
-            throw new IOException("data path is not specified!");
+        if ( path == null )
+        {
+            throw new IOException( "data path is not specified!" );
         }
         }
 
 
-        final File dataPath = new File(path);
-        if (!dataPath.exists()) {
-            throw new IOException("data path '" + dataPath + "' does not exist");
+        final File dataPath = new File( path );
+        if ( !dataPath.exists() )
+        {
+            throw new IOException( "data path '" + dataPath + "' does not exist" );
         }
         }
 
 
-        final File stoagePath = new File(dataPath.getAbsolutePath() + File.separator + "storage");
-        stoagePath.mkdir();
+        final File storagePath = new File( dataPath.getAbsolutePath() + File.separator + "storage" );
+        mkdirs( storagePath );
 
 
         final EnvironmentConfig environmentConfig = new EnvironmentConfig();
         final EnvironmentConfig environmentConfig = new EnvironmentConfig();
-        environment = Environments.newInstance(stoagePath.getAbsolutePath(), environmentConfig);
+        environment = Environments.newInstance( storagePath.getAbsolutePath(), environmentConfig );
 
 
-        environment.executeInTransaction(txn -> store
-                = environment.openStore("store1", StoreConfig.WITHOUT_DUPLICATES, txn));
+        environment.executeInTransaction( txn -> store
+                = environment.openStore( "store1", StoreConfig.WITHOUT_DUPLICATES, txn ) );
     }
     }
 
 
-    public void store(final TelemetryPublishBean bean) {
-        if (bean == null) {
+    public void store( final TelemetryPublishBean bean )
+    {
+        if ( bean == null )
+        {
             return;
             return;
         }
         }
 
 
         final String instanceHash = bean.getInstanceHash();
         final String instanceHash = bean.getInstanceHash();
-        if (instanceHash != null) {
-            final TelemetryPublishBean existingBean = get(instanceHash);
+        if ( instanceHash != null )
+        {
+            final TelemetryPublishBean existingBean = get( instanceHash );
             Instant existingTimestamp = null;
             Instant existingTimestamp = null;
-            if (existingBean != null) {
+            if ( existingBean != null )
+            {
                 existingTimestamp = existingBean.getTimestamp();
                 existingTimestamp = existingBean.getTimestamp();
             }
             }
-            if (existingTimestamp == null || existingTimestamp.isBefore(bean.getTimestamp())) {
-                put(bean);
+            if ( existingTimestamp == null || existingTimestamp.isBefore( bean.getTimestamp() ) )
+            {
+                put( bean );
             }
             }
         }
         }
     }
     }
 
 
-    public Iterator<TelemetryPublishBean> iterator() {
+    public Iterator<TelemetryPublishBean> iterator( )
+    {
         return new InnerIterator();
         return new InnerIterator();
     }
     }
 
 
-    private boolean put(final TelemetryPublishBean value) {
-        return environment.computeInTransaction(transaction -> {
-            final ByteIterable k = StringBinding.stringToEntry(value.getInstanceHash());
-            final ByteIterable v = StringBinding.stringToEntry(JsonUtil.serialize(value));
-            return store.put(transaction,k,v);
-        });
+    private boolean put( final TelemetryPublishBean value )
+    {
+        return environment.computeInTransaction( transaction ->
+        {
+            final ByteIterable k = StringBinding.stringToEntry( value.getInstanceHash() );
+            final ByteIterable v = StringBinding.stringToEntry( JsonUtil.serialize( value ) );
+            return store.put( transaction, k, v );
+        } );
     }
     }
 
 
-    private TelemetryPublishBean get(final String hash) {
-        return environment.computeInTransaction(transaction -> {
-            final ByteIterable k = StringBinding.stringToEntry(hash);
-            final ByteIterable v = store.get(transaction,k);
-            if (v != null) {
-                final String string = StringBinding.entryToString(new ArrayByteIterable(v));
-                if (!StringUtil.isEmpty(string)) {
-                    return JsonUtil.deserialize(string, TelemetryPublishBean.class);
+    private TelemetryPublishBean get( final String hash )
+    {
+        return environment.computeInTransaction( transaction ->
+        {
+            final ByteIterable k = StringBinding.stringToEntry( hash );
+            final ByteIterable v = store.get( transaction, k );
+            if ( v != null )
+            {
+                final String string = StringBinding.entryToString( new ArrayByteIterable( v ) );
+                if ( !StringUtil.isEmpty( string ) )
+                {
+                    return JsonUtil.deserialize( string, TelemetryPublishBean.class );
                 }
                 }
             }
             }
             return null;
             return null;
-        });
+        } );
     }
     }
 
 
-    public void close() {
+    public void close( )
+    {
         store.getEnvironment().close();
         store.getEnvironment().close();
     }
     }
 
 
-    public long count() {
+    public long count( )
+    {
         return environment.computeInTransaction( transaction -> store.count( transaction ) );
         return environment.computeInTransaction( transaction -> store.count( transaction ) );
     }
     }
 
 
-    private class InnerIterator implements AutoCloseable,Iterator {
+    private class InnerIterator implements AutoCloseable, Iterator
+    {
         private final Transaction transaction;
         private final Transaction transaction;
         private final Cursor cursor;
         private final Cursor cursor;
 
 
         private boolean closed;
         private boolean closed;
         private String nextValue = "";
         private String nextValue = "";
 
 
-        InnerIterator() {
+        InnerIterator( )
+        {
             this.transaction = environment.beginReadonlyTransaction();
             this.transaction = environment.beginReadonlyTransaction();
-            this.cursor = store.openCursor(transaction);
+            this.cursor = store.openCursor( transaction );
             doNext();
             doNext();
         }
         }
 
 
-        private void doNext() {
-            try {
-                if (closed) {
+        private void doNext( )
+        {
+            try
+            {
+                if ( closed )
+                {
                     return;
                     return;
                 }
                 }
 
 
-                if (!cursor.getNext()) {
+                if ( !cursor.getNext() )
+                {
                     close();
                     close();
                     return;
                     return;
                 }
                 }
                 final ByteIterable nextKey = cursor.getKey();
                 final ByteIterable nextKey = cursor.getKey();
-                final String string = StringBinding.entryToString(new ArrayByteIterable(nextKey));
+                final String string = StringBinding.entryToString( new ArrayByteIterable( nextKey ) );
 
 
-                if (string == null || string.isEmpty()) {
+                if ( string == null || string.isEmpty() )
+                {
                     close();
                     close();
                     return;
                     return;
                 }
                 }
                 nextValue = string;
                 nextValue = string;
-            } catch (Exception e) {
+            }
+            catch ( Exception e )
+            {
                 e.printStackTrace();
                 e.printStackTrace();
                 throw e;
                 throw e;
             }
             }
         }
         }
 
 
         @Override
         @Override
-        public void close() {
-            if (closed) {
+        public void close( )
+        {
+            if ( closed )
+            {
                 return;
                 return;
             }
             }
             cursor.close();
             cursor.close();
@@ -168,20 +196,31 @@ public class Storage {
         }
         }
 
 
         @Override
         @Override
-        public boolean hasNext() {
+        public boolean hasNext( )
+        {
             return !closed && nextValue != null;
             return !closed && nextValue != null;
         }
         }
 
 
         @Override
         @Override
-        public TelemetryPublishBean next() {
+        public TelemetryPublishBean next( )
+        {
             final String value = nextValue;
             final String value = nextValue;
             doNext();
             doNext();
-            return get(value);
+            return get( value );
         }
         }
 
 
         @Override
         @Override
-        public void remove() {
-            throw new UnsupportedOperationException("remove not supported");
+        public void remove( )
+        {
+            throw new UnsupportedOperationException( "remove not supported" );
+        }
+    }
+
+    static void mkdirs( final File file ) throws IOException
+    {
+        if ( !file.mkdirs() )
+        {
+            throw new IOException( "unable to create path " + file.getAbsolutePath() );
         }
         }
     }
     }
 
 

+ 100 - 82
data-service/src/main/java/password/pwm/receiver/SummaryBean.java

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  * http://www.pwm-project.org
  *
  *
  * Copyright (c) 2006-2009 Novell, Inc.
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@
  * You should have received a copy of the GNU General Public License
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
  */
  */
 
 
 package password.pwm.receiver;
 package password.pwm.receiver;
@@ -39,88 +38,97 @@ import java.util.TreeMap;
 
 
 @Getter
 @Getter
 @Builder
 @Builder
-public class SummaryBean {
+public class SummaryBean
+{
     private int serverCount;
     private int serverCount;
-    private Map<String,SiteSummary> siteSummary;
-    private Map<String,Integer> ldapVendorCount;
-    private Map<String,Integer> appServerCount;
-    private Map<String,Integer> settingCount;
-    private Map<String,Integer> statCount;
-    private Map<String,Integer> osCount;
-    private Map<String,Integer> dbCount;
-    private Map<String,Integer> javaCount;
-    private Map<String,Integer> ssprVersionCount;
-
-    static SummaryBean fromStorage(final Storage storage, final TimeDuration maxAge ) {
+    private Map<String, SiteSummary> siteSummary;
+    private Map<String, Integer> ldapVendorCount;
+    private Map<String, Integer> appServerCount;
+    private Map<String, Integer> settingCount;
+    private Map<String, Integer> statCount;
+    private Map<String, Integer> osCount;
+    private Map<String, Integer> dbCount;
+    private Map<String, Integer> javaCount;
+    private Map<String, Integer> ssprVersionCount;
+
+    static SummaryBean fromStorage( final Storage storage, final TimeDuration maxAge )
+    {
 
 
         final String naText = "n/a";
         final String naText = "n/a";
 
 
         int serverCount = 0;
         int serverCount = 0;
-        final Map<String,SiteSummary> siteSummaryMap = new TreeMap<>();
-        final Map<String,Integer> ldapVendorCount = new TreeMap<>();
-        final Map<String,Integer> appServerCount = new TreeMap<>();
-        final Map<String,Integer> settingCount = new TreeMap<>();
-        final Map<String,Integer> statCount = new TreeMap<>();
-        final Map<String,Integer> osCount = new TreeMap<>();
-        final Map<String,Integer> dbCount = new TreeMap<>();
-        final Map<String,Integer> javaCount = new TreeMap<>();
-        final Map<String,Integer> ssprVersionCount = new TreeMap<>();
-
-        for (Iterator<TelemetryPublishBean> iterator = storage.iterator(); iterator.hasNext(); ) {
+        final Map<String, SiteSummary> siteSummaryMap = new TreeMap<>();
+        final Map<String, Integer> ldapVendorCount = new TreeMap<>();
+        final Map<String, Integer> appServerCount = new TreeMap<>();
+        final Map<String, Integer> settingCount = new TreeMap<>();
+        final Map<String, Integer> statCount = new TreeMap<>();
+        final Map<String, Integer> osCount = new TreeMap<>();
+        final Map<String, Integer> dbCount = new TreeMap<>();
+        final Map<String, Integer> javaCount = new TreeMap<>();
+        final Map<String, Integer> ssprVersionCount = new TreeMap<>();
+
+        for ( Iterator<TelemetryPublishBean> iterator = storage.iterator(); iterator.hasNext(); )
+        {
             final TelemetryPublishBean bean = iterator.next();
             final TelemetryPublishBean bean = iterator.next();
             final TimeDuration age = TimeDuration.fromCurrent( bean.getTimestamp() );
             final TimeDuration age = TimeDuration.fromCurrent( bean.getTimestamp() );
 
 
-            if (bean.getAbout() != null && age.isShorterThan( maxAge ) ) {
+            if ( bean.getAbout() != null && age.isShorterThan( maxAge ) )
+            {
                 serverCount++;
                 serverCount++;
                 final String hashID = bean.getInstanceHash();
                 final String hashID = bean.getInstanceHash();
                 final String ldapVendor = bean.getLdapVendorName() == null
                 final String ldapVendor = bean.getLdapVendorName() == null
                         ? naText
                         ? naText
                         : bean.getLdapVendorName();
                         : bean.getLdapVendorName();
 
 
-                final String dbVendor = dbVendorName(bean);
+                final String dbVendor = dbVendorName( bean );
 
 
                 final SiteSummary siteSummary = SiteSummary.builder()
                 final SiteSummary siteSummary = SiteSummary.builder()
-                        .description(bean.getSiteDescription())
-                        .version(bean.getVersionVersion())
-                        .installAge(TimeDuration.fromCurrent(bean.getInstallTime()).asDuration())
-                        .updateAge(TimeDuration.fromCurrent(bean.getTimestamp()).asDuration())
-                        .ldapVendor(ldapVendor)
-                        .osName(bean.getAbout().get(PwmAboutProperty.java_osName.name()))
-                        .osVersion(bean.getAbout().get(PwmAboutProperty.java_osVersion.name()))
-                        .servletName(bean.getAbout().get(PwmAboutProperty.java_appServerInfo.name()))
-                        .dbVendor(dbVendor)
-                        .appliance(Boolean.parseBoolean(bean.getAbout().get(PwmAboutProperty.app_mode_appliance.name())))
-                        .javaVm(javaVmInfo( bean, "n/a" ))
+                        .description( bean.getSiteDescription() )
+                        .version( bean.getVersionVersion() )
+                        .installAge( TimeDuration.fromCurrent( bean.getInstallTime() ).asDuration() )
+                        .updateAge( TimeDuration.fromCurrent( bean.getTimestamp() ).asDuration() )
+                        .ldapVendor( ldapVendor )
+                        .osName( bean.getAbout().get( PwmAboutProperty.java_osName.name() ) )
+                        .osVersion( bean.getAbout().get( PwmAboutProperty.java_osVersion.name() ) )
+                        .servletName( bean.getAbout().get( PwmAboutProperty.java_appServerInfo.name() ) )
+                        .dbVendor( dbVendor )
+                        .appliance( Boolean.parseBoolean( bean.getAbout().get( PwmAboutProperty.app_mode_appliance.name() ) ) )
+                        .javaVm( javaVmInfo( bean, "n/a" ) )
                         .build();
                         .build();
 
 
-                siteSummaryMap.put(hashID, siteSummary);
+                siteSummaryMap.put( hashID, siteSummary );
 
 
-                incrementCounterMap(dbCount, dbVendor);
+                incrementCounterMap( dbCount, dbVendor );
 
 
-                incrementCounterMap(ldapVendorCount, ldapVendor);
+                incrementCounterMap( ldapVendorCount, ldapVendor );
 
 
-                incrementCounterMap(appServerCount, siteSummary.getServletName());
+                incrementCounterMap( appServerCount, siteSummary.getServletName() );
 
 
-                incrementCounterMap(osCount, bean.getAbout().get(PwmAboutProperty.java_osName.name()));
+                incrementCounterMap( osCount, bean.getAbout().get( PwmAboutProperty.java_osName.name() ) );
 
 
-                incrementCounterMap(javaCount, siteSummary.getJavaVm());
+                incrementCounterMap( javaCount, siteSummary.getJavaVm() );
 
 
-                incrementCounterMap(ssprVersionCount, siteSummary.getVersion());
+                incrementCounterMap( ssprVersionCount, siteSummary.getVersion() );
 
 
-                for (final String settingKey : bean.getConfiguredSettings()) {
-                    final PwmSetting setting = PwmSetting.forKey(settingKey);
-                    if (setting != null) {
-                        final String description = setting.toMenuLocationDebug(null, null);
-                        incrementCounterMap(settingCount, description);
+                for ( final String settingKey : bean.getConfiguredSettings() )
+                {
+                    final PwmSetting setting = PwmSetting.forKey( settingKey );
+                    if ( setting != null )
+                    {
+                        final String description = setting.toMenuLocationDebug( null, null );
+                        incrementCounterMap( settingCount, description );
                     }
                     }
                 }
                 }
 
 
-                for (final String statKey : bean.getStatistics().keySet()) {
-                    final Statistic statistic = Statistic.forKey(statKey);
-                    if (statistic != null) {
-                        if (statistic.getType() == Statistic.Type.INCREMENTOR) {
-                            final int count = Integer.parseInt(bean.getStatistics().get(statKey));
-                            incrementCounterMap(statCount, statistic.getLabel(null), count);
+                for ( final String statKey : bean.getStatistics().keySet() )
+                {
+                    final Statistic statistic = Statistic.forKey( statKey );
+                    if ( statistic != null )
+                    {
+                        if ( statistic.getType() == Statistic.Type.INCREMENTER )
+                        {
+                            final int count = Integer.parseInt( bean.getStatistics().get( statKey ) );
+                            incrementCounterMap( statCount, statistic.getLabel( null ), count );
                         }
                         }
                     }
                     }
                 }
                 }
@@ -129,46 +137,55 @@ public class SummaryBean {
 
 
 
 
         return SummaryBean.builder()
         return SummaryBean.builder()
-                .serverCount(serverCount)
-                .siteSummary(siteSummaryMap)
-                .ldapVendorCount(ldapVendorCount)
-                .settingCount(settingCount)
-                .statCount(statCount)
-                .appServerCount(appServerCount)
-                .osCount(osCount)
-                .dbCount(dbCount)
-                .javaCount(javaCount)
-                .ssprVersionCount(ssprVersionCount)
+                .serverCount( serverCount )
+                .siteSummary( siteSummaryMap )
+                .ldapVendorCount( ldapVendorCount )
+                .settingCount( settingCount )
+                .statCount( statCount )
+                .appServerCount( appServerCount )
+                .osCount( osCount )
+                .dbCount( dbCount )
+                .javaCount( javaCount )
+                .ssprVersionCount( ssprVersionCount )
                 .build();
                 .build();
 
 
     }
     }
 
 
-    private static void incrementCounterMap(final Map<String,Integer> map, final String key) {
-        incrementCounterMap(map, key, 1);
+    private static void incrementCounterMap( final Map<String, Integer> map, final String key )
+    {
+        incrementCounterMap( map, key, 1 );
     }
     }
 
 
-    private static void incrementCounterMap(final Map<String,Integer> map, final String key, final int count) {
-        if (map.containsKey(key)) {
-            map.put(key, map.get(key) + count);
-        } else {
-            map.put(key, count);
+    private static void incrementCounterMap( final Map<String, Integer> map, final String key, final int count )
+    {
+        if ( map.containsKey( key ) )
+        {
+            map.put( key, map.get( key ) + count );
+        }
+        else
+        {
+            map.put( key, count );
         }
         }
     }
     }
 
 
-    private static String dbVendorName(final TelemetryPublishBean bean) {
+    private static String dbVendorName( final TelemetryPublishBean bean )
+    {
         String dbVendor = "n/a";
         String dbVendor = "n/a";
-        final Map<String,String> aboutMap = bean.getAbout();
-        if (aboutMap.get(PwmAboutProperty.database_databaseProductName.name()) != null) {
-            dbVendor = aboutMap.get(PwmAboutProperty.database_databaseProductName.name());
-
-            if (aboutMap.get(PwmAboutProperty.database_databaseProductVersion.name()) != null) {
-                dbVendor += "/" + aboutMap.get(PwmAboutProperty.database_databaseProductVersion.name());
+        final Map<String, String> aboutMap = bean.getAbout();
+        if ( aboutMap.get( PwmAboutProperty.database_databaseProductName.name() ) != null )
+        {
+            dbVendor = aboutMap.get( PwmAboutProperty.database_databaseProductName.name() );
+
+            if ( aboutMap.get( PwmAboutProperty.database_databaseProductVersion.name() ) != null )
+            {
+                dbVendor += "/" + aboutMap.get( PwmAboutProperty.database_databaseProductVersion.name() );
             }
             }
         }
         }
         return dbVendor;
         return dbVendor;
     }
     }
 
 
-    private static String javaVmInfo(final TelemetryPublishBean bean, final String naText ) {
+    private static String javaVmInfo( final TelemetryPublishBean bean, final String naText )
+    {
         return bean.getAbout().getOrDefault( PwmAboutProperty.java_vmName.name(), naText )
         return bean.getAbout().getOrDefault( PwmAboutProperty.java_vmName.name(), naText )
                 + " ("
                 + " ("
                 + bean.getAbout().getOrDefault( PwmAboutProperty.java_vmVendor.name(), naText )
                 + bean.getAbout().getOrDefault( PwmAboutProperty.java_vmVendor.name(), naText )
@@ -178,7 +195,8 @@ public class SummaryBean {
 
 
     @Value
     @Value
     @Builder
     @Builder
-    public static class SiteSummary {
+    public static class SiteSummary
+    {
         private String description;
         private String description;
         private String version;
         private String version;
         private Duration installAge;
         private Duration installAge;

+ 39 - 27
data-service/src/main/java/password/pwm/receiver/TelemetryRestReceiver.java

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  * http://www.pwm-project.org
  *
  *
  * Copyright (c) 2006-2009 Novell, Inc.
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@
  * You should have received a copy of the GNU General Public License
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
  */
  */
 
 
 package password.pwm.receiver;
 package password.pwm.receiver;
@@ -44,33 +43,39 @@ import java.io.Reader;
 import java.io.StringWriter;
 import java.io.StringWriter;
 
 
 @WebServlet(
 @WebServlet(
-        name="TelemetryRestReceiver",
-        urlPatterns={
+        name = "TelemetryRestReceiver",
+        urlPatterns = {
                 "/telemetry",
                 "/telemetry",
         }
         }
 )
 )
 
 
-public class TelemetryRestReceiver extends HttpServlet {
+public class TelemetryRestReceiver extends HttpServlet
+{
     @Override
     @Override
-    protected void doPost(final HttpServletRequest req, final HttpServletResponse resp)
+    protected void doPost( final HttpServletRequest req, final HttpServletResponse resp )
             throws ServletException, IOException
             throws ServletException, IOException
     {
     {
-        try {
-            resp.setHeader("Content","application/json");
-            final String input = readRequestBodyAsString(req, 1024 * 1024);
-            final TelemetryPublishBean telemetryPublishBean = JsonUtil.deserialize(input, TelemetryPublishBean.class);
-            final Storage stoage = ContextManager.getContextManager(this.getServletContext()).getApp().getStorage();
-            stoage.store(telemetryPublishBean);
-            resp.getWriter().print(RestResultBean.forSuccessMessage(null, null, null, Message.Success_Unknown).toJson());
-        } catch (PwmUnrecoverableException e) {
-            resp.getWriter().print(RestResultBean.fromError(e.getErrorInformation()).toJson());
-        } catch (Exception e) {
-            final RestResultBean restResultBean = RestResultBean.fromError(new ErrorInformation(PwmError.ERROR_UNKNOWN, e.getMessage()));
-            resp.getWriter().print(restResultBean.toJson());
+        try
+        {
+            resp.setHeader( "Content", "application/json" );
+            final String input = readRequestBodyAsString( req, 1024 * 1024 );
+            final TelemetryPublishBean telemetryPublishBean = JsonUtil.deserialize( input, TelemetryPublishBean.class );
+            final Storage stoage = ContextManager.getContextManager( this.getServletContext() ).getApp().getStorage();
+            stoage.store( telemetryPublishBean );
+            resp.getWriter().print( RestResultBean.forSuccessMessage( null, null, null, Message.Success_Unknown ).toJson() );
+        }
+        catch ( PwmUnrecoverableException e )
+        {
+            resp.getWriter().print( RestResultBean.fromError( e.getErrorInformation() ).toJson() );
+        }
+        catch ( Exception e )
+        {
+            final RestResultBean restResultBean = RestResultBean.fromError( new ErrorInformation( PwmError.ERROR_UNKNOWN, e.getMessage() ) );
+            resp.getWriter().print( restResultBean.toJson() );
         }
         }
     }
     }
 
 
-    private static String readRequestBodyAsString(final HttpServletRequest req, final int maxChars)
+    private static String readRequestBodyAsString( final HttpServletRequest req, final int maxChars )
             throws IOException, PwmUnrecoverableException
             throws IOException, PwmUnrecoverableException
     {
     {
         final StringWriter stringWriter = new StringWriter();
         final StringWriter stringWriter = new StringWriter();
@@ -79,18 +84,25 @@ public class TelemetryRestReceiver extends HttpServlet {
                 PwmConstants.DEFAULT_CHARSET
                 PwmConstants.DEFAULT_CHARSET
         );
         );
 
 
-        try {
-            IOUtils.copy(readerStream, stringWriter);
-        } catch (Exception e) {
+        try
+        {
+            IOUtils.copy( readerStream, stringWriter );
+        }
+        catch ( Exception e )
+        {
             final String errorMsg = "error reading request body stream: " + e.getMessage();
             final String errorMsg = "error reading request body stream: " + e.getMessage();
-            throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNKNOWN,errorMsg));
-        } finally {
-            IOUtils.closeQuietly(readerStream);
+            throw new PwmUnrecoverableException( new ErrorInformation( PwmError.ERROR_UNKNOWN, errorMsg ) );
+        }
+        finally
+        {
+            IOUtils.closeQuietly( readerStream );
         }
         }
 
 
         final String stringValue = stringWriter.toString();
         final String stringValue = stringWriter.toString();
-        if (stringValue.length() > maxChars) {
-            throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNKNOWN,"input request body is to big, size=" + stringValue.length() + ", max=" + maxChars));
+        if ( stringValue.length() > maxChars )
+        {
+            final String msg = "input request body is to big, size=" + stringValue.length() + ", max=" + maxChars;
+            throw new PwmUnrecoverableException( new ErrorInformation( PwmError.ERROR_UNKNOWN, msg ) );
         }
         }
         return stringValue;
         return stringValue;
     }
     }

+ 15 - 14
data-service/src/main/java/password/pwm/receiver/TelemetryViewerServlet.java

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  * http://www.pwm-project.org
  *
  *
  * Copyright (c) 2006-2009 Novell, Inc.
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@
  * You should have received a copy of the GNU General Public License
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
  */
  */
 
 
 package password.pwm.receiver;
 package password.pwm.receiver;
@@ -35,37 +34,39 @@ import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeUnit;
 
 
 @WebServlet(
 @WebServlet(
-        name="TelemetryViewer",
-        urlPatterns={
+        name = "TelemetryViewer",
+        urlPatterns = {
                 "/viewer",
                 "/viewer",
         }
         }
 )
 )
-public class TelemetryViewerServlet extends HttpServlet {
+public class TelemetryViewerServlet extends HttpServlet
+{
     private static final String PARAM_DAYS = "days";
     private static final String PARAM_DAYS = "days";
 
 
-    public static String SUMMARY_ATTR = "SummaryBean";
+    public static final String SUMMARY_ATTR = "SummaryBean";
 
 
     @Override
     @Override
-    protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException
+    protected void doGet( final HttpServletRequest req, final HttpServletResponse resp ) throws ServletException, IOException
     {
     {
         final String daysString = req.getParameter( PARAM_DAYS );
         final String daysString = req.getParameter( PARAM_DAYS );
         final int days = StringUtil.isEmpty( daysString ) ? 30 : Integer.parseInt( daysString );
         final int days = StringUtil.isEmpty( daysString ) ? 30 : Integer.parseInt( daysString );
-        final ContextManager contextManager = ContextManager.getContextManager(req.getServletContext());
+        final ContextManager contextManager = ContextManager.getContextManager( req.getServletContext() );
         final PwmReceiverApp app = contextManager.getApp();
         final PwmReceiverApp app = contextManager.getApp();
 
 
         {
         {
             final String errorState = app.getStatus().getErrorState();
             final String errorState = app.getStatus().getErrorState();
-            if (!StringUtil.isEmpty(errorState)) {
-                resp.sendError(500, errorState);
+            if ( !StringUtil.isEmpty( errorState ) )
+            {
+                resp.sendError( 500, errorState );
                 final String htmlBody = "<html>Error: " + errorState + "</html>";
                 final String htmlBody = "<html>Error: " + errorState + "</html>";
-                resp.getWriter().print(htmlBody);
+                resp.getWriter().print( htmlBody );
                 return;
                 return;
             }
             }
         }
         }
 
 
         final Storage storage = app.getStorage();
         final Storage storage = app.getStorage();
-        final SummaryBean summaryBean = SummaryBean.fromStorage(storage, new TimeDuration(days, TimeUnit.DAYS ) );
-        req.setAttribute(SUMMARY_ATTR, summaryBean);
-        req.getServletContext().getRequestDispatcher("/WEB-INF/jsp/telemetry-viewer.jsp").forward(req,resp);
+        final SummaryBean summaryBean = SummaryBean.fromStorage( storage, new TimeDuration( days, TimeUnit.DAYS ) );
+        req.setAttribute( SUMMARY_ATTR, summaryBean );
+        req.getServletContext().getRequestDispatcher( "/WEB-INF/jsp/telemetry-viewer.jsp" ).forward( req, resp );
     }
     }
 }
 }

+ 0 - 0
data-service/src/main/resources/password/pwm/receiver/package-info.java


+ 1 - 5
data-service/src/main/webapp/META-INF/context.xml

@@ -1,11 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <?xml version="1.0" encoding="UTF-8"?>
-
 <!--
 <!--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
-  ~ http://www.pwm-project.org
-  ~
   ~ Copyright (c) 2006-2009 Novell, Inc.
   ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2017 The PWM Project
+  ~ Copyright (c) 2009-2018 The PWM Project
   ~
   ~
   ~ This program is free software; you can redistribute it and/or modify
   ~ This program is free software; you can redistribute it and/or modify
   ~ it under the terms of the GNU General Public License as published by
   ~ it under the terms of the GNU General Public License as published by
@@ -20,7 +17,6 @@
   ~ You should have received a copy of the GNU General Public License
   ~ You should have received a copy of the GNU General Public License
   ~ along with this program; if not, write to the Free Software
   ~ along with this program; if not, write to the Free Software
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-  ~
   -->
   -->
 
 
 <Context tldValidation="false" unloadDelay="30000" useHttpOnly="true">
 <Context tldValidation="false" unloadDelay="30000" useHttpOnly="true">

+ 22 - 27
data-service/src/main/webapp/WEB-INF/jsp/telemetry-viewer.jsp

@@ -1,35 +1,30 @@
-<%@ page import="password.pwm.config.PwmSetting" %>
+<%--
+ ~ Password Management Servlets (PWM)
+ ~ http://www.pwm-project.org
+ ~
+ ~ Copyright (c) 2006-2009 Novell, Inc.
+ ~ Copyright (c) 2009-2018 The PWM Project
+ ~
+ ~ This program is free software; you can redistribute it and/or modify
+ ~ it under the terms of the GNU General Public License as published by
+ ~ the Free Software Foundation; either version 2 of the License, or
+ ~ (at your option) any later version.
+ ~
+ ~ This program is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ ~ GNU General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU General Public License
+ ~ along with this program; if not, write to the Free Software
+ ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+--%>
+
 <%@ page import="password.pwm.receiver.SummaryBean" %>
 <%@ page import="password.pwm.receiver.SummaryBean" %>
 <%@ page import="password.pwm.receiver.TelemetryViewerServlet" %>
 <%@ page import="password.pwm.receiver.TelemetryViewerServlet" %>
-<%@ page import="org.joda.time.DateTime" %>
-<%@ page import="java.time.format.DateTimeFormatter" %>
 <%@ page import="java.time.Instant" %>
 <%@ page import="java.time.Instant" %>
-<%@ page import="java.time.LocalDateTime" %>
-<%@ page import="java.time.format.FormatStyle" %>
 <%@ page import="password.pwm.receiver.PwmReceiverApp" %>
 <%@ page import="password.pwm.receiver.PwmReceiverApp" %>
 <%@ page import="password.pwm.receiver.ContextManager" %>
 <%@ page import="password.pwm.receiver.ContextManager" %>
-<%--
-  ~ Password Management Servlets (PWM)
-  ~ http://www.pwm-project.org
-  ~
-  ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2017 The PWM Project
-  ~
-  ~ This program is free software; you can redistribute it and/or modify
-  ~ it under the terms of the GNU General Public License as published by
-  ~ the Free Software Foundation; either version 2 of the License, or
-  ~ (at your option) any later version.
-  ~
-  ~ This program is distributed in the hope that it will be useful,
-  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
-  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  ~ GNU General Public License for more details.
-  ~
-  ~ You should have received a copy of the GNU General Public License
-  ~ along with this program; if not, write to the Free Software
-  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-  ~
-  --%>
 
 
 <!DOCTYPE html>
 <!DOCTYPE html>
 <%@ page contentType="text/html" %>
 <%@ page contentType="text/html" %>

+ 3 - 13
data-service/src/main/webapp/WEB-INF/web.xml

@@ -1,10 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 <!--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
-  ~ http://www.pwm-project.org
-  ~
   ~ Copyright (c) 2006-2009 Novell, Inc.
   ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2017 The PWM Project
+  ~ Copyright (c) 2009-2018 The PWM Project
   ~
   ~
   ~ This program is free software; you can redistribute it and/or modify
   ~ This program is free software; you can redistribute it and/or modify
   ~ it under the terms of the GNU General Public License as published by
   ~ it under the terms of the GNU General Public License as published by
@@ -19,23 +17,15 @@
   ~ You should have received a copy of the GNU General Public License
   ~ You should have received a copy of the GNU General Public License
   ~ along with this program; if not, write to the Free Software
   ~ along with this program; if not, write to the Free Software
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-  ~
   -->
   -->
 
 
 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns="http://java.sun.com/xml/ns/javaee"
          xmlns="http://java.sun.com/xml/ns/javaee"
          xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
          xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
          id="PWM" version="3.0">
          id="PWM" version="3.0">
-    <display-name>PWM Receiver</display-name>
+    <display-name>PWM Data Service</display-name>
     <!-- <distributable/> Clustering/Session replication is not supported -->
     <!-- <distributable/> Clustering/Session replication is not supported -->
-    <description>Password Management Servlet</description>
-    <context-param>
-        <description>
-            Explicit location of application path working directory or the literal value "unspecified".  See the environment documentation at /public/reference/environment.jsp for more information.
-        </description>
-        <param-name>applicationPath</param-name>
-        <param-value>unspecified</param-value>
-    </context-param>
+    <description>Password Management Servlet - Data Service</description>
     <welcome-file-list>
     <welcome-file-list>
         <welcome-file>index.jsp</welcome-file>
         <welcome-file>index.jsp</welcome-file>
     </welcome-file-list>
     </welcome-file-list>

+ 20 - 21
data-service/src/main/webapp/index.jsp

@@ -1,25 +1,24 @@
 <%--
 <%--
-  ~ Password Management Servlets (PWM)
-  ~ http://www.pwm-project.org
-  ~
-  ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2017 The PWM Project
-  ~
-  ~ This program is free software; you can redistribute it and/or modify
-  ~ it under the terms of the GNU General Public License as published by
-  ~ the Free Software Foundation; either version 2 of the License, or
-  ~ (at your option) any later version.
-  ~
-  ~ This program is distributed in the hope that it will be useful,
-  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
-  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  ~ GNU General Public License for more details.
-  ~
-  ~ You should have received a copy of the GNU General Public License
-  ~ along with this program; if not, write to the Free Software
-  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-  ~
-  --%>
+ ~ Password Management Servlets (PWM)
+ ~ http://www.pwm-project.org
+ ~
+ ~ Copyright (c) 2006-2009 Novell, Inc.
+ ~ Copyright (c) 2009-2018 The PWM Project
+ ~
+ ~ This program is free software; you can redistribute it and/or modify
+ ~ it under the terms of the GNU General Public License as published by
+ ~ the Free Software Foundation; either version 2 of the License, or
+ ~ (at your option) any later version.
+ ~
+ ~ This program is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ ~ GNU General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU General Public License
+ ~ along with this program; if not, write to the Free Software
+ ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+--%>
 
 
 <!DOCTYPE html>
 <!DOCTYPE html>
 <%@ page language="java" session="true" isThreadSafe="true"
 <%@ page language="java" session="true" isThreadSafe="true"

+ 41 - 47
docker/pom.xml

@@ -9,7 +9,7 @@
 
 
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
 
 
-    <packaging>pom</packaging>
+    <packaging>jar</packaging>
 
 
     <artifactId>pwm-docker</artifactId>
     <artifactId>pwm-docker</artifactId>
 
 
@@ -17,67 +17,49 @@
 
 
     <properties>
     <properties>
         <skipDocker>false</skipDocker>
         <skipDocker>false</skipDocker>
+        <project.root.basedir>${project.basedir}/..</project.root.basedir>
     </properties>
     </properties>
 
 
-    <profiles>
-        <profile>
-            <id>skip-docker</id>
-            <properties>
-                <skipDocker>true</skipDocker>
-            </properties>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-        </profile>
-    </profiles>
-
     <build>
     <build>
         <plugins>
         <plugins>
             <plugin>
             <plugin>
-                <groupId>io.fabric8</groupId>
-                <artifactId>docker-maven-plugin</artifactId>
-                <version>0.26.0</version>
+                <groupId>com.google.cloud.tools</groupId>
+                <artifactId>jib-maven-plugin</artifactId>
+                <version>0.9.10</version>
                 <executions>
                 <executions>
                     <execution>
                     <execution>
+                        <id>make-docker-image</id>
                         <phase>package</phase>
                         <phase>package</phase>
                         <goals>
                         <goals>
-                            <goal>build</goal>
-                            <goal>save</goal>
+                            <goal>buildTar</goal>
                         </goals>
                         </goals>
+                        <configuration>
+                            <to>
+                                <image>${dockerImageTag}</image>
+                            </to>
+                            <container>
+                                <jvmFlags>
+                                    <jvmFlag>-Xms1g</jvmFlag>
+                                    <jvmFlag>-Xmx1g</jvmFlag>
+                                </jvmFlags>
+                                <mainClass>password.pwm.onejar.OnejarMain</mainClass>
+                                <args>
+                                    <arg>-applicationPath</arg>
+                                    <arg>/config</arg>
+                                </args>
+                                <format>docker</format>
+                                <ports>8443</ports>
+                            </container>
+                            <!--<useCurrentTimestamp>true</useCurrentTimestamp>-->
+                            <allowInsecureRegistries>true</allowInsecureRegistries>
+                        </configuration>
                     </execution>
                     </execution>
                 </executions>
                 </executions>
-                <configuration>
-                    <skip>${skipDocker}</skip>
-                    <saveFile>${project.build.directory}/pwm-server-docker-${project.version}.tar.gz</saveFile>
-                    <images>
-                        <image>
-                            <name>pwm-server:${project.version}</name>
-                            <build>
-                                <tags>
-                                    <tag>pwm-server</tag>
-                                </tags>
-                                <dockerFileDir>${project.basedir}/src/main/docker</dockerFileDir>
-                                <assembly>
-                                    <name>onejar</name>
-                                    <inline>
-                                        <files>
-                                            <file>
-                                                <source>..${file.separator}onejar${file.separator}target${file.separator}pwm-onejar-${project.version}.jar</source>
-                                                <destName>pwm-onejar.jar</destName>
-                                            </file>
-                                        </files>
-                                    </inline>
-                                </assembly>
-                            </build>
-                        </image>
-                    </images>
-                </configuration>
             </plugin>
             </plugin>
         </plugins>
         </plugins>
     </build>
     </build>
 
 
     <dependencies>
     <dependencies>
-
         <!--
         <!--
         This is included via the assembly plugin descriptor, so its not really required here but keeps
         This is included via the assembly plugin descriptor, so its not really required here but keeps
         the module build order correct.
         the module build order correct.
@@ -87,8 +69,20 @@
             <artifactId>pwm-onejar</artifactId>
             <artifactId>pwm-onejar</artifactId>
             <version>${project.version}</version>
             <version>${project.version}</version>
             <type>jar</type>
             <type>jar</type>
-            <scope>provided</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.tomcat.embed</groupId>
+                    <artifactId>tomcat-embed-core</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.tomcat.embed</groupId>
+                    <artifactId>tomcat-embed-jasper</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>commons-cli</groupId>
+                    <artifactId>commons-cli</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
         </dependency>
-
     </dependencies>
     </dependencies>
 </project>
 </project>

+ 0 - 11
docker/readme.txt

@@ -1,11 +0,0 @@
-Docker image usage notes:
-
---Load docker image from file
-docker load --input=pwm-docker-image.tar.gz
-
---Create docker container and run--
-docker run -d --name <container name> -p 8443:8443 pwm
-
-This will expose the https port to 8443.  You can also manage the exposed configuration volume of /config if you want to preserve
-the /config directory when you destroy/create the container in the future.  The docker image will place all of it's configuration
-and runtime data in the /config volume.

+ 28 - 0
docker/src/main/java/password/pwm/docker/DockerStub.java

@@ -0,0 +1,28 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2018 The PWM Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package password.pwm.docker;
+
+// at least one class is required to "trick" the jib-docker plugin to execute.
+public class DockerStub
+{
+}

+ 1 - 0
docker/src/main/jib/config/readme.txt

@@ -0,0 +1 @@
+# readme

+ 1 - 1
onejar/onejar-assembly.xml

@@ -38,7 +38,7 @@
     </dependencySets>
     </dependencySets>
     <files>
     <files>
         <file>
         <file>
-            <source>${project.basedir}${file.separator}..${file.separator}server${file.separator}target${file.separator}${warArtifactID}</source>
+            <source>${project.basedir}${file.separator}..${file.separator}webapp${file.separator}target${file.separator}${warArtifactID}</source>
             <outputDirectory>/</outputDirectory>
             <outputDirectory>/</outputDirectory>
             <destName>embed.war</destName>
             <destName>embed.war</destName>
         </file>
         </file>

+ 7 - 38
onejar/pom.xml

@@ -13,41 +13,21 @@
 
 
     <packaging>jar</packaging>
     <packaging>jar</packaging>
 
 
-    <name>PWM Password Self Service: Executable Jar</name>
+    <name>PWM Password Self Service: Executable JAR</name>
 
 
     <properties>
     <properties>
-        <tomcat.version>9.0.10</tomcat.version>
-        <maven.compiler.source>1.8</maven.compiler.source>
-        <maven.compiler.target>1.8</maven.compiler.target>
+        <project.root.basedir>${project.basedir}/..</project.root.basedir>
+        <tomcat.version>9.0.12</tomcat.version>
+        <jetty-version>9.4.11.v20180605</jetty-version>
     </properties>
     </properties>
 
 
     <build>
     <build>
         <plugins>
         <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-enforcer-plugin</artifactId>
-                <version>3.0.0-M1</version>
-                <executions>
-                    <execution>
-                        <id>enforce-maven</id>
-                        <goals>
-                            <goal>enforce</goal>
-                        </goals>
-                        <configuration>
-                            <rules>
-                                <requireMavenVersion>
-                                    <version>${pwm.minimum.maven.version}</version>
-                                </requireMavenVersion>
-                            </rules>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
             <plugin>
             <plugin>
                 <!-- prevent normal jar from being built -->
                 <!-- prevent normal jar from being built -->
                 <groupId>org.apache.maven.plugins</groupId>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-jar-plugin</artifactId>
                 <artifactId>maven-jar-plugin</artifactId>
-                <version>3.0.2</version>
+                <version>3.1.0</version>
                 <executions>
                 <executions>
                     <execution>
                     <execution>
                         <id>default-jar</id>
                         <id>default-jar</id>
@@ -59,21 +39,10 @@
                     </execution>
                     </execution>
                 </executions>
                 </executions>
             </plugin>
             </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-compiler-plugin</artifactId>
-                <version>3.7.0</version>
-                <configuration>
-                    <source>${maven.compiler.source}</source>
-                    <target>${maven.compiler.target}</target>
-                </configuration>
-            </plugin>
-
             <plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-assembly-plugin</artifactId>
                 <artifactId>maven-assembly-plugin</artifactId>
                 <version>3.1.0</version>
                 <version>3.1.0</version>
-
                 <configuration>
                 <configuration>
                     <appendAssemblyId>false</appendAssemblyId>
                     <appendAssemblyId>false</appendAssemblyId>
                     <descriptors>
                     <descriptors>
@@ -81,7 +50,7 @@
                     </descriptors>
                     </descriptors>
                     <archive>
                     <archive>
                         <manifestEntries>
                         <manifestEntries>
-                            <Main-Class>password.pwm.onejar.TomcatOneJarMain</Main-Class>
+                            <Main-Class>password.pwm.onejar.OnejarMain</Main-Class>
                             <Implementation-Title>${project.name}</Implementation-Title>
                             <Implementation-Title>${project.name}</Implementation-Title>
                             <Implementation-Version>${project.version}</Implementation-Version>
                             <Implementation-Version>${project.version}</Implementation-Version>
                             <Implementation-Vendor>${project.organization.name}</Implementation-Vendor>
                             <Implementation-Vendor>${project.organization.name}</Implementation-Vendor>
@@ -138,4 +107,4 @@
             <version>1.4</version>
             <version>1.4</version>
         </dependency>
         </dependency>
     </dependencies>
     </dependencies>
-</project>
+</project>

+ 68 - 41
onejar/src/main/java/password/pwm/onejar/ArgumentParser.java

@@ -45,8 +45,8 @@ public class ArgumentParser
 {
 {
     private static final String ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
     private static final String ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
 
 
-    public TomcatConfig parseArguments( final String[] args )
-            throws ArgumentParserException, TomcatOneJarException
+    public OnejarConfig parseArguments( final String[] args )
+            throws ArgumentParserException, OnejarException
     {
     {
         if ( args == null || args.length == 0 )
         if ( args == null || args.length == 0 )
         {
         {
@@ -67,7 +67,7 @@ public class ArgumentParser
 
 
             if ( commandLine.hasOption( Argument.version.name() ) )
             if ( commandLine.hasOption( Argument.version.name() ) )
             {
             {
-                TomcatOneJarMain.out( TomcatOneJarMain.getVersion() );
+                OnejarMain.output( TomcatOnejarRunner.getVersion() );
                 return null;
                 return null;
             }
             }
             else if ( commandLine.hasOption( Argument.help.name() ) )
             else if ( commandLine.hasOption( Argument.help.name() ) )
@@ -91,16 +91,16 @@ public class ArgumentParser
                 {
                 {
                     argumentMap = mapFromCommandLine( commandLine );
                     argumentMap = mapFromCommandLine( commandLine );
                 }
                 }
-                final TomcatConfig tomcatConfig;
+                final OnejarConfig onejarConfig;
                 try
                 try
                 {
                 {
-                    tomcatConfig = makeTomcatConfig( argumentMap );
+                    onejarConfig = makeTomcatConfig( argumentMap );
                 }
                 }
                 catch ( IOException e )
                 catch ( IOException e )
                 {
                 {
                     throw new ArgumentParserException( "error while reading input: " + e.getMessage() );
                     throw new ArgumentParserException( "error while reading input: " + e.getMessage() );
                 }
                 }
-                return tomcatConfig;
+                return onejarConfig;
             }
             }
         }
         }
 
 
@@ -110,9 +110,9 @@ public class ArgumentParser
     private Map<Argument, String> mapFromProperties( final String filename ) throws ArgumentParserException
     private Map<Argument, String> mapFromProperties( final String filename ) throws ArgumentParserException
     {
     {
         final Properties props = new Properties();
         final Properties props = new Properties();
-        try
+        try ( InputStream is = new FileInputStream( new File( filename ) ) )
         {
         {
-            props.load( new FileInputStream( new File( filename ) ) );
+            props.load( is );
         }
         }
         catch ( IOException e )
         catch ( IOException e )
         {
         {
@@ -154,49 +154,62 @@ public class ArgumentParser
     }
     }
 
 
 
 
-    private TomcatConfig makeTomcatConfig( final Map<Argument, String> argumentMap ) throws IOException, ArgumentParserException
+    private OnejarConfig makeTomcatConfig( final Map<Argument, String> argumentMap )
+            throws IOException, ArgumentParserException
     {
     {
-        final TomcatConfig tomcatConfig = new TomcatConfig();
-        tomcatConfig.setKeystorePass( genRandomString( 32 ) );
-        tomcatConfig.setApplicationPath( parseFileOption( argumentMap, Argument.applicationPath ) );
+        final OnejarConfig.OnejarConfigBuilder onejarConfig = OnejarConfig.builder();
+        onejarConfig.keystorePass( genRandomString( 32 ) );
+        onejarConfig.applicationPath( parseFileOption( argumentMap, Argument.applicationPath ) );
 
 
-        tomcatConfig.setContext( argumentMap.getOrDefault( Argument.context, Resource.defaultContext.getValue() ) );
+        final String context = argumentMap.getOrDefault( Argument.context, Resource.defaultContext.getValue() );
+        onejarConfig.context( context );
 
 
         if ( argumentMap.containsKey( Argument.war ) )
         if ( argumentMap.containsKey( Argument.war ) )
         {
         {
             final File inputWarFile = new File( argumentMap.get( Argument.war ) );
             final File inputWarFile = new File( argumentMap.get( Argument.war ) );
             if ( !inputWarFile.exists() )
             if ( !inputWarFile.exists() )
             {
             {
-                System.out.println( "output war file " + inputWarFile.getAbsolutePath() + "does not exist" );
-                System.exit( -1 );
-                return null;
+                final String msg = "output war file " + inputWarFile.getAbsolutePath() + "does not exist";
+                System.out.println( msg );
+                throw new IllegalStateException( msg );
             }
             }
-            tomcatConfig.setWar( new FileInputStream( inputWarFile ) );
+            onejarConfig.war( new FileInputStream( inputWarFile ) );
         }
         }
         else
         else
         {
         {
-            tomcatConfig.setWar( getEmbeddedWar() );
+            onejarConfig.war( getEmbeddedWar() );
         }
         }
 
 
-        tomcatConfig.setPort( Integer.parseInt( Resource.defaultPort.getValue() ) );
-        if ( argumentMap.containsKey( Argument.port ) )
+        final int port;
         {
         {
-            try
+            final int defaultPort = Integer.parseInt( Resource.defaultPort.getValue() );
+            if ( argumentMap.containsKey( Argument.port ) )
             {
             {
-                tomcatConfig.setPort( Integer.parseInt( argumentMap.get( Argument.port ) ) );
+                try
+                {
+                    port = Integer.parseInt( argumentMap.get( Argument.port ) );
+                    onejarConfig.port( port );
+                }
+                catch ( NumberFormatException e )
+                {
+                    final String msg = Argument.port.name() + " argument must be numeric";
+                    System.out.println( msg );
+                    throw new IllegalStateException( msg );
+                }
             }
             }
-            catch ( NumberFormatException e )
+            else
             {
             {
-                System.out.println( Argument.port.name() + " argument must be numeric" );
-                System.exit( -1 );
+                port = defaultPort;
             }
             }
         }
         }
+        onejarConfig.port( port );
 
 
-        tomcatConfig.setLocalAddress( argumentMap.getOrDefault( Argument.localAddress, Resource.defaultLocalAddress.getValue() ) );
+        final String localAddress = argumentMap.getOrDefault( Argument.localAddress, Resource.defaultLocalAddress.getValue() );
+        onejarConfig.localAddress( localAddress );
 
 
         try
         try
         {
         {
-            final ServerSocket socket = new ServerSocket( tomcatConfig.getPort(), 100, InetAddress.getByName( tomcatConfig.getLocalAddress() ) );
+            final ServerSocket socket = new ServerSocket( port, 100, InetAddress.getByName( localAddress ) );
             socket.close();
             socket.close();
         }
         }
         catch ( Exception e )
         catch ( Exception e )
@@ -206,21 +219,21 @@ public class ArgumentParser
 
 
         if ( argumentMap.containsKey( Argument.workPath ) )
         if ( argumentMap.containsKey( Argument.workPath ) )
         {
         {
-            tomcatConfig.setWorkingPath( parseFileOption( argumentMap, Argument.workPath ) );
+            onejarConfig.workingPath( parseFileOption( argumentMap, Argument.workPath ) );
         }
         }
         else
         else
         {
         {
-            tomcatConfig.setWorkingPath( figureDefaultWorkPath( tomcatConfig ) );
+            onejarConfig.workingPath( figureDefaultWorkPath( localAddress, context, port ) );
         }
         }
 
 
-        return tomcatConfig;
+        return onejarConfig.build();
     }
     }
 
 
 
 
-    private static void outputHelp( ) throws TomcatOneJarException
+    private static void outputHelp( ) throws OnejarException
     {
     {
         final HelpFormatter formatter = new HelpFormatter();
         final HelpFormatter formatter = new HelpFormatter();
-        System.out.println( TomcatOneJarMain.getVersion() );
+        System.out.println( TomcatOnejarRunner.getVersion() );
         System.out.println( "usage:" );
         System.out.println( "usage:" );
         formatter.printOptions(
         formatter.printOptions(
                 System.console().writer(),
                 System.console().writer(),
@@ -249,33 +262,39 @@ public class ArgumentParser
         return file;
         return file;
     }
     }
 
 
-    private static File figureDefaultWorkPath( final TomcatConfig tomcatConfig ) throws ArgumentParserException
+    private static File figureDefaultWorkPath(
+            final String localAddress,
+            final String context,
+            final int port
+    )
+            throws ArgumentParserException, IOException
     {
     {
         final String userHomePath = System.getProperty( "user.home" );
         final String userHomePath = System.getProperty( "user.home" );
         if ( userHomePath != null && !userHomePath.isEmpty() )
         if ( userHomePath != null && !userHomePath.isEmpty() )
         {
         {
             final File basePath = new File( userHomePath + File.separator
             final File basePath = new File( userHomePath + File.separator
                     + Resource.defaultWorkPathName.getValue() );
                     + Resource.defaultWorkPathName.getValue() );
-            basePath.mkdir();
+
+            mkdirs( basePath );
 
 
             final String workPath;
             final String workPath;
             {
             {
                 String workPathStr = basePath.getPath() + File.separator + "work"
                 String workPathStr = basePath.getPath() + File.separator + "work"
                         + "-"
                         + "-"
-                        + escapeFilename( tomcatConfig.getContext() )
+                        + escapeFilename( context )
                         + "-"
                         + "-"
-                        + escapeFilename( Integer.toString( tomcatConfig.getPort() ) );
+                        + escapeFilename( Integer.toString( port ) );
 
 
-                if ( tomcatConfig.getLocalAddress() != null && !tomcatConfig.getLocalAddress().isEmpty() )
+                if ( localAddress != null && !localAddress.isEmpty() )
                 {
                 {
-                    workPathStr += "-" + escapeFilename( tomcatConfig.getLocalAddress() );
+                    workPathStr += "-" + escapeFilename( localAddress );
 
 
                 }
                 }
                 workPath = workPathStr;
                 workPath = workPathStr;
             }
             }
             final File workFile = new File( workPath );
             final File workFile = new File( workPath );
-            workFile.mkdirs();
-            TomcatOneJarMain.out( "using work directory: " + workPath );
+            mkdirs( workFile );
+            OnejarMain.output( "using work directory: " + workPath );
             return workFile;
             return workFile;
         }
         }
 
 
@@ -284,7 +303,7 @@ public class ArgumentParser
 
 
     private static InputStream getEmbeddedWar( ) throws IOException, ArgumentParserException
     private static InputStream getEmbeddedWar( ) throws IOException, ArgumentParserException
     {
     {
-        final Class clazz = TomcatOneJarMain.class;
+        final Class clazz = TomcatOnejarRunner.class;
         final String className = clazz.getSimpleName() + ".class";
         final String className = clazz.getSimpleName() + ".class";
         final String classPath = clazz.getResource( className ).toString();
         final String classPath = clazz.getResource( className ).toString();
         if ( !classPath.startsWith( "jar" ) )
         if ( !classPath.startsWith( "jar" ) )
@@ -311,4 +330,12 @@ public class ArgumentParser
         }
         }
         return stringBuilder.toString();
         return stringBuilder.toString();
     }
     }
+
+    static void mkdirs( final File file ) throws IOException
+    {
+        if ( !file.mkdirs() && !file.exists() )
+        {
+            throw new IOException( "unable to create path " + file.getAbsolutePath() );
+        }
+    }
 }
 }

+ 58 - 0
onejar/src/main/java/password/pwm/onejar/OnejarConfig.java

@@ -0,0 +1,58 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2018 The PWM Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package password.pwm.onejar;
+
+import lombok.Builder;
+import lombok.Value;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+@Value
+@Builder
+class OnejarConfig
+{
+    private int port;
+    private File applicationPath;
+    private File workingPath;
+    private InputStream war;
+    private String context;
+    private String localAddress;
+    private String keystorePass;
+
+    File getWarFolder( ) throws IOException
+    {
+        return new File( this.getWorkingPath().getAbsoluteFile() + File.separator + "war" );
+    }
+
+    File getKeystoreFile( )
+    {
+        return new File( this.getWorkingPath().getAbsoluteFile() + File.separator + "keystore" );
+    }
+
+    File getPwmAppPropertiesFile( )
+    {
+        return new File( this.getWorkingPath().getAbsoluteFile() + File.separator + "application.properties" );
+    }
+}

+ 2 - 2
onejar/src/main/java/password/pwm/onejar/TomcatOneJarException.java → onejar/src/main/java/password/pwm/onejar/OnejarException.java

@@ -22,9 +22,9 @@
 
 
 package password.pwm.onejar;
 package password.pwm.onejar;
 
 
-public class TomcatOneJarException extends Exception
+public class OnejarException extends Exception
 {
 {
-    public TomcatOneJarException( final String msg )
+    public OnejarException( final String msg )
     {
     {
         super( msg );
         super( msg );
     }
     }

+ 83 - 0
onejar/src/main/java/password/pwm/onejar/OnejarMain.java

@@ -0,0 +1,83 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2018 The PWM Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package password.pwm.onejar;
+
+import javax.servlet.ServletException;
+import java.io.IOException;
+import java.time.Duration;
+import java.time.Instant;
+
+public class OnejarMain
+{
+    //private static final String TEMP_WAR_FILE_NAME = "embed.war";
+    static final String KEYSTORE_ALIAS = "https";
+
+
+    public static void main( final String[] args )
+    {
+        final ArgumentParser argumentParser = new ArgumentParser();
+        OnejarConfig onejarConfig = null;
+        try
+        {
+            onejarConfig = argumentParser.parseArguments( args );
+        }
+        catch ( ArgumentParserException | OnejarException e )
+        {
+            output( "error parsing command line: " + e.getMessage() );
+        }
+
+        final OnejarMain onejarMain = new OnejarMain();
+        onejarMain.run( onejarConfig );
+    }
+
+    void run( final OnejarConfig onejarConfig )
+    {
+        final TomcatOnejarRunner runner = new TomcatOnejarRunner( this );
+        final Instant startTime = Instant.now();
+
+        if ( onejarConfig != null )
+        {
+            try
+            {
+                runner.startTomcat( onejarConfig );
+            }
+            catch ( OnejarException | ServletException | IOException e )
+            {
+                out( "error starting tomcat: " + e.getMessage() );
+            }
+        }
+
+        final Duration duration = Duration.between( startTime, Instant.now() );
+        out( "exiting after " + duration.toString() );
+    }
+
+    void out( final String output )
+    {
+        output( output );
+    }
+
+    static void output( final String output )
+    {
+        System.out.println( output );
+    }
+}

+ 0 - 107
onejar/src/main/java/password/pwm/onejar/TomcatConfig.java

@@ -1,107 +0,0 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2018 The PWM Project
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-package password.pwm.onejar;
-
-import java.io.File;
-import java.io.InputStream;
-
-class TomcatConfig
-{
-    private int port;
-    private File applicationPath;
-    private File workingPath;
-    private InputStream war;
-    private String context;
-    private String localAddress;
-    private String keystorePass;
-
-    public int getPort( )
-    {
-        return port;
-    }
-
-    public void setPort( final int port )
-    {
-        this.port = port;
-    }
-
-    public File getApplicationPath( )
-    {
-        return applicationPath;
-    }
-
-    public void setApplicationPath( final File applicationPath )
-    {
-        this.applicationPath = applicationPath;
-    }
-
-    public File getWorkingPath( )
-    {
-        return workingPath;
-    }
-
-    public void setWorkingPath( final File workingPath )
-    {
-        this.workingPath = workingPath;
-    }
-
-    public InputStream getWar( )
-    {
-        return war;
-    }
-
-    public void setWar( final InputStream war )
-    {
-        this.war = war;
-    }
-
-    public String getContext( )
-    {
-        return context;
-    }
-
-    public void setContext( final String context )
-    {
-        this.context = context;
-    }
-
-    public String getLocalAddress( )
-    {
-        return localAddress;
-    }
-
-    public void setLocalAddress( final String localAddress )
-    {
-        this.localAddress = localAddress;
-    }
-
-    public String getKeystorePass( )
-    {
-        return keystorePass;
-    }
-
-    public void setKeystorePass( final String keystorePass )
-    {
-        this.keystorePass = keystorePass;
-    }
-}

+ 118 - 97
onejar/src/main/java/password/pwm/onejar/TomcatOneJarMain.java → onejar/src/main/java/password/pwm/onejar/TomcatOnejarRunner.java

@@ -28,17 +28,23 @@ import org.apache.catalina.startup.Tomcat;
 import org.apache.catalina.util.ServerInfo;
 import org.apache.catalina.util.ServerInfo;
 
 
 import javax.servlet.ServletException;
 import javax.servlet.ServletException;
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.File;
-import java.io.FileWriter;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Method;
 import java.net.URL;
 import java.net.URL;
 import java.net.URLClassLoader;
 import java.net.URLClassLoader;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.FileVisitOption;
 import java.nio.file.FileVisitOption;
 import java.nio.file.Files;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.time.Duration;
 import java.time.Duration;
 import java.time.Instant;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.ArrayList;
@@ -47,61 +53,26 @@ import java.util.List;
 import java.util.Properties;
 import java.util.Properties;
 import java.util.jar.Attributes;
 import java.util.jar.Attributes;
 import java.util.jar.Manifest;
 import java.util.jar.Manifest;
+import java.util.stream.Collectors;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 import java.util.zip.ZipInputStream;
 
 
-public class TomcatOneJarMain
+public class TomcatOnejarRunner
 {
 {
-    //private static final String TEMP_WAR_FILE_NAME = "embed.war";
-    private static final String KEYSTORE_ALIAS = "https";
+    final OnejarMain onejarMain;
 
 
-    public static void main( final String[] args )
+    public TomcatOnejarRunner( final OnejarMain onejarMain )
     {
     {
-        final ArgumentParser argumentParser = new ArgumentParser();
-        TomcatConfig tomcatConfig = null;
-        try
-        {
-            tomcatConfig = argumentParser.parseArguments( args );
-        }
-        catch ( ArgumentParserException | TomcatOneJarException e )
-        {
-            out( "error parsing command line: " + e.getMessage() );
-        }
-        if ( tomcatConfig != null )
-        {
-            try
-            {
-                startTomcat( tomcatConfig );
-            }
-            catch ( TomcatOneJarException | ServletException | IOException e )
-            {
-                out( "error starting tomcat: " + e.getMessage() );
-            }
-        }
+        this.onejarMain = onejarMain;
     }
     }
 
 
-    private static File getWarFolder( final TomcatConfig tomcatConfig ) throws IOException
+    private void explodeWar( final OnejarConfig onejarConfig ) throws IOException
     {
     {
-        return new File( tomcatConfig.getWorkingPath().getAbsoluteFile() + File.separator + "war" );
-    }
-
-    private static File getKeystoreFile( final TomcatConfig tomcatConfig )
-    {
-        return new File( tomcatConfig.getWorkingPath().getAbsoluteFile() + File.separator + "keystore" );
-    }
-
-    private static File getPwmAppPropertiesFile( final TomcatConfig tomcatConfig )
-    {
-        return new File( tomcatConfig.getWorkingPath().getAbsoluteFile() + File.separator + "application.properties" );
-    }
-
-
-    private static void explodeWar( final TomcatConfig tomcatConfig ) throws IOException
-    {
-        final InputStream warSource = tomcatConfig.getWar();
+        final InputStream warSource = onejarConfig.getWar();
         final ZipInputStream zipInputStream = new ZipInputStream( warSource );
         final ZipInputStream zipInputStream = new ZipInputStream( warSource );
-        final File outputFolder = getWarFolder( tomcatConfig );
-        outputFolder.mkdir();
+        final File outputFolder = onejarConfig.getWarFolder( );
+
+        ArgumentParser.mkdirs( outputFolder );
 
 
         ZipEntry zipEntry = zipInputStream.getNextEntry();
         ZipEntry zipEntry = zipInputStream.getNextEntry();
 
 
@@ -112,7 +83,7 @@ public class TomcatOneJarMain
 
 
             if ( !zipEntry.isDirectory() )
             if ( !zipEntry.isDirectory() )
             {
             {
-                newFile.getParentFile().mkdirs();
+                ArgumentParser.mkdirs( newFile.getParentFile() );
                 Files.copy( zipInputStream, newFile.toPath() );
                 Files.copy( zipInputStream, newFile.toPath() );
             }
             }
             zipEntry = zipInputStream.getNextEntry();
             zipEntry = zipInputStream.getNextEntry();
@@ -120,98 +91,124 @@ public class TomcatOneJarMain
 
 
     }
     }
 
 
-    private static void startTomcat( final TomcatConfig tomcatConfig ) throws ServletException, IOException, TomcatOneJarException
+    void startTomcat( final OnejarConfig onejarConfig )
+            throws ServletException, IOException, OnejarException
     {
     {
         final Instant startTime = Instant.now();
         final Instant startTime = Instant.now();
 
 
-        purgeDirectory( tomcatConfig.getWorkingPath().toPath() );
+        purgeDirectory( onejarConfig.getWorkingPath().toPath() );
 
 
-        explodeWar( tomcatConfig );
+        explodeWar( onejarConfig );
         out( "deployed war" );
         out( "deployed war" );
 
 
         try
         try
         {
         {
-            generatePwmKeystore( tomcatConfig );
+            generatePwmKeystore( onejarConfig );
             out( "keystore generated" );
             out( "keystore generated" );
         }
         }
         catch ( Exception e )
         catch ( Exception e )
         {
         {
-            throw new TomcatOneJarException( "error generating keystore: " + e.getMessage() );
+            throw new OnejarException( "error generating keystore: " + e.getMessage() );
         }
         }
 
 
-        outputPwmAppProperties( tomcatConfig );
+        outputPwmAppProperties( onejarConfig );
 
 
-        setupEnv( tomcatConfig );
+        setupEnv( onejarConfig );
 
 
         final Tomcat tomcat = new Tomcat();
         final Tomcat tomcat = new Tomcat();
 
 
         {
         {
-            final File basePath = new File( tomcatConfig.getWorkingPath().getPath() + File.separator + "b" );
-            basePath.mkdir();
+            final File basePath = new File( onejarConfig.getWorkingPath().getPath() + File.separator + "b" );
+            ArgumentParser.mkdirs( basePath );
             tomcat.setBaseDir( basePath.getAbsolutePath() );
             tomcat.setBaseDir( basePath.getAbsolutePath() );
         }
         }
         {
         {
-            final File basePath = new File( tomcatConfig.getWorkingPath().getPath() + File.separator + "a" );
-            basePath.mkdir();
+            final File basePath = new File( onejarConfig.getWorkingPath().getPath() + File.separator + "a" );
+            ArgumentParser.mkdirs( basePath );
             tomcat.getServer().setCatalinaBase( basePath );
             tomcat.getServer().setCatalinaBase( basePath );
             tomcat.getServer().setCatalinaHome( basePath );
             tomcat.getServer().setCatalinaHome( basePath );
         }
         }
         {
         {
-            final File workPath = new File( tomcatConfig.getWorkingPath().getPath() + File.separator + "w" );
-            workPath.mkdir();
+            final File workPath = new File( onejarConfig.getWorkingPath().getPath() + File.separator + "w" );
+            ArgumentParser.mkdirs( workPath );
             tomcat.getHost().setAppBase( workPath.getAbsolutePath() );
             tomcat.getHost().setAppBase( workPath.getAbsolutePath() );
         }
         }
 
 
-
         tomcat.getHost().setAutoDeploy( false );
         tomcat.getHost().setAutoDeploy( false );
         tomcat.getHost().setDeployOnStartup( false );
         tomcat.getHost().setDeployOnStartup( false );
 
 
-        final String warPath = getWarFolder( tomcatConfig ).getAbsolutePath();
-        tomcat.addWebapp( "/" + tomcatConfig.getContext(), warPath );
+        deployRedirectConnector( tomcat, onejarConfig );
+
+        final String warPath = onejarConfig.getWarFolder().getAbsolutePath();
+        tomcat.addWebapp( "/" + onejarConfig.getContext(), warPath );
+
 
 
         try
         try
         {
         {
             tomcat.start();
             tomcat.start();
 
 
-            tomcat.setConnector( makeConnector( tomcatConfig ) );
+            tomcat.setConnector( makeConnector( onejarConfig ) );
 
 
             out( "tomcat started in " + Duration.between( Instant.now(), startTime ).toString() );
             out( "tomcat started in " + Duration.between( Instant.now(), startTime ).toString() );
         }
         }
         catch ( LifecycleException e )
         catch ( LifecycleException e )
         {
         {
-            throw new TomcatOneJarException( "unable to start tomcat: " + e.getMessage() );
+            throw new OnejarException( "unable to start tomcat: " + e.getMessage() );
         }
         }
-
         tomcat.getServer().await();
         tomcat.getServer().await();
 
 
-        System.out.println( "\n" );
+        System.out.println( "\nexiting..." );
+    }
+
+    private void deployRedirectConnector( final Tomcat tomcat, final OnejarConfig onejarConfig )
+            throws IOException, ServletException
+    {
+        final String srcRootWebXml = "ROOT-redirect-webapp/WEB-INF/web.xml";
+        final String srcRootIndex = "ROOT-redirect-webapp/WEB-INF/index.jsp";
+
+        final File redirBase = new File( onejarConfig.getWorkingPath().getAbsoluteFile() + File.separator + "redirectBase" );
+        ArgumentParser.mkdirs( redirBase );
+        {
+            ArgumentParser.mkdirs( new File ( redirBase.getAbsolutePath() + File.separator + "WEB-INF" ) );
+            copyFileAndReplace(
+                    srcRootWebXml,
+                    redirBase.getPath() + File.separator + "WEB-INF" + File.separator + "web.xml",
+                    onejarConfig.getContext() );
+            copyFileAndReplace(
+                    srcRootIndex,
+                    redirBase.getPath() + File.separator +  "WEB-INF" + File.separator + "index.jsp",
+                    onejarConfig.getContext() );
+        }
+
+        tomcat.addWebapp( "", redirBase.getAbsolutePath() );
     }
     }
 
 
-    private static Connector makeConnector( final TomcatConfig tomcatConfig )
+
+    private Connector makeConnector( final OnejarConfig onejarConfig )
     {
     {
         final Connector connector = new Connector( "HTTP/1.1" );
         final Connector connector = new Connector( "HTTP/1.1" );
-        connector.setPort( tomcatConfig.getPort() );
+        connector.setPort( onejarConfig.getPort() );
 
 
-        if ( tomcatConfig.getLocalAddress() != null && !tomcatConfig.getLocalAddress().isEmpty() )
+        if ( onejarConfig.getLocalAddress() != null && !onejarConfig.getLocalAddress().isEmpty() )
         {
         {
-            connector.setProperty( "address", tomcatConfig.getLocalAddress() );
+            connector.setProperty( "address", onejarConfig.getLocalAddress() );
         }
         }
         connector.setSecure( true );
         connector.setSecure( true );
         connector.setScheme( "https" );
         connector.setScheme( "https" );
         connector.setAttribute( "SSLEnabled", "true" );
         connector.setAttribute( "SSLEnabled", "true" );
-        connector.setAttribute( "keystoreFile", getKeystoreFile( tomcatConfig ).getAbsolutePath() );
-        connector.setAttribute( "keystorePass", tomcatConfig.getKeystorePass() );
-        connector.setAttribute( "keyAlias", KEYSTORE_ALIAS );
+        connector.setAttribute( "keystoreFile", onejarConfig.getKeystoreFile().getAbsolutePath() );
+        connector.setAttribute( "keystorePass", onejarConfig.getKeystorePass() );
+        connector.setAttribute( "keyAlias", OnejarMain.KEYSTORE_ALIAS );
         connector.setAttribute( "clientAuth", "false" );
         connector.setAttribute( "clientAuth", "false" );
 
 
         return connector;
         return connector;
     }
     }
 
 
-    static String getVersion( ) throws TomcatOneJarException
+     static String getVersion( ) throws OnejarException
     {
     {
         try
         try
         {
         {
-            final Class clazz = TomcatOneJarMain.class;
+            final Class clazz = TomcatOnejarRunner.class;
             final String className = clazz.getSimpleName() + ".class";
             final String className = clazz.getSimpleName() + ".class";
             final String classPath = clazz.getResource( className ).toString();
             final String classPath = clazz.getResource( className ).toString();
             if ( !classPath.startsWith( "jar" ) )
             if ( !classPath.startsWith( "jar" ) )
@@ -228,11 +225,11 @@ public class TomcatOneJarMain
         }
         }
         catch ( IOException e )
         catch ( IOException e )
         {
         {
-            throw new TomcatOneJarException( "error reading internal version info: " + e.getMessage() );
+            throw new OnejarException( "error reading internal version info: " + e.getMessage() );
         }
         }
     }
     }
 
 
-    private static void purgeDirectory( final Path rootPath )
+    private void purgeDirectory( final Path rootPath )
             throws IOException
             throws IOException
     {
     {
         System.out.println( "purging work directory: " + rootPath );
         System.out.println( "purging work directory: " + rootPath );
@@ -244,54 +241,78 @@ public class TomcatOneJarMain
     }
     }
 
 
 
 
-    static void out( final String output )
+    void out( final String output )
     {
     {
-        System.out.println( output );
+        onejarMain.out( output );
     }
     }
 
 
 
 
-    static void generatePwmKeystore( final TomcatConfig tomcatConfig )
+    void generatePwmKeystore( final OnejarConfig onejarConfig )
             throws IOException, ClassNotFoundException, IllegalAccessException, NoSuchMethodException, InvocationTargetException
             throws IOException, ClassNotFoundException, IllegalAccessException, NoSuchMethodException, InvocationTargetException
     {
     {
-        final File warPath = getWarFolder( tomcatConfig );
-        final String keystoreFile = getKeystoreFile( tomcatConfig ).getAbsolutePath();
+        final File warPath = onejarConfig.getWarFolder();
+        final String keystoreFile = onejarConfig.getKeystoreFile().getAbsolutePath();
         final File webInfPath = new File( warPath.getAbsolutePath() + File.separator + "WEB-INF" + File.separator + "lib" );
         final File webInfPath = new File( warPath.getAbsolutePath() + File.separator + "WEB-INF" + File.separator + "lib" );
         final File[] jarFiles = webInfPath.listFiles();
         final File[] jarFiles = webInfPath.listFiles();
         final List<URL> jarURLList = new ArrayList<>();
         final List<URL> jarURLList = new ArrayList<>();
-        for ( final File jarFile : jarFiles )
+        if ( jarFiles != null )
         {
         {
-            jarURLList.add( jarFile.toURI().toURL() );
+            for ( final File jarFile : jarFiles )
+            {
+                jarURLList.add( jarFile.toURI().toURL() );
+            }
         }
         }
         final URLClassLoader classLoader = URLClassLoader.newInstance( jarURLList.toArray( new URL[ jarURLList.size() ] ) );
         final URLClassLoader classLoader = URLClassLoader.newInstance( jarURLList.toArray( new URL[ jarURLList.size() ] ) );
         final Class pwmMainClass = classLoader.loadClass( "password.pwm.util.cli.MainClass" );
         final Class pwmMainClass = classLoader.loadClass( "password.pwm.util.cli.MainClass" );
         final Method mainMethod = pwmMainClass.getMethod( "main", String[].class );
         final Method mainMethod = pwmMainClass.getMethod( "main", String[].class );
         final String[] arguments = new String[] {
         final String[] arguments = new String[] {
-                "-applicationPath=" + tomcatConfig.getApplicationPath().getAbsolutePath(),
+                "-applicationPath=" + onejarConfig.getApplicationPath().getAbsolutePath(),
                 "ExportHttpsKeyStore",
                 "ExportHttpsKeyStore",
                 keystoreFile,
                 keystoreFile,
-                KEYSTORE_ALIAS,
-                tomcatConfig.getKeystorePass(),
+                OnejarMain.KEYSTORE_ALIAS,
+                onejarConfig.getKeystorePass(),
         };
         };
 
 
         mainMethod.invoke( null, ( Object ) arguments );
         mainMethod.invoke( null, ( Object ) arguments );
         classLoader.close();
         classLoader.close();
     }
     }
 
 
-    static void setupEnv( final TomcatConfig tomcatConfig )
+    void setupEnv( final OnejarConfig onejarConfig )
     {
     {
         final String envVarPrefix = Resource.envVarPrefix.getValue();
         final String envVarPrefix = Resource.envVarPrefix.getValue();
-        System.setProperty( envVarPrefix + "_APPLICATIONPATH", tomcatConfig.getApplicationPath().getAbsolutePath() );
+        System.setProperty( envVarPrefix + "_APPLICATIONPATH", onejarConfig.getApplicationPath().getAbsolutePath() );
         System.setProperty( envVarPrefix + "_APPLICATIONFLAGS", "ManageHttps" );
         System.setProperty( envVarPrefix + "_APPLICATIONFLAGS", "ManageHttps" );
-        System.setProperty( envVarPrefix + "_APPLICATIONPARAMFILE", getPwmAppPropertiesFile( tomcatConfig ).getAbsolutePath() );
+        System.setProperty( envVarPrefix + "_APPLICATIONPARAMFILE", onejarConfig.getPwmAppPropertiesFile().getAbsolutePath() );
     }
     }
 
 
-    static void outputPwmAppProperties( final TomcatConfig tomcatConfig ) throws IOException
+    void outputPwmAppProperties( final OnejarConfig onejarConfig ) throws IOException
     {
     {
         final Properties properties = new Properties();
         final Properties properties = new Properties();
-        properties.setProperty( "AutoExportHttpsKeyStoreFile", getKeystoreFile( tomcatConfig ).getAbsolutePath() );
-        properties.setProperty( "AutoExportHttpsKeyStorePassword", tomcatConfig.getKeystorePass() );
-        properties.setProperty( "AutoExportHttpsKeyStoreAlias", KEYSTORE_ALIAS );
-        final File propFile = getPwmAppPropertiesFile( tomcatConfig );
-        properties.store( new FileWriter( propFile ), "auto-generated file" );
+        properties.setProperty( "AutoExportHttpsKeyStoreFile", onejarConfig.getKeystoreFile().getAbsolutePath() );
+        properties.setProperty( "AutoExportHttpsKeyStorePassword", onejarConfig.getKeystorePass() );
+        properties.setProperty( "AutoExportHttpsKeyStoreAlias", OnejarMain.KEYSTORE_ALIAS );
+        final File propFile = onejarConfig.getPwmAppPropertiesFile( );
+        try ( Writer writer = new OutputStreamWriter( new FileOutputStream( propFile ), StandardCharsets.UTF_8 ) )
+        {
+            properties.store( writer, "auto-generated file" );
+        }
+    }
+
+    void copyFileAndReplace(
+            final String srcPath,
+            final String destPath,
+            final String rootcontext
+    )
+            throws IOException
+    {
+        try ( InputStream inputStream = TomcatOnejarRunner.class.getClassLoader().getResourceAsStream( srcPath ) )
+        {
+            try ( BufferedReader reader = new BufferedReader( new InputStreamReader( inputStream, "UTF8" ) ) )
+            {
+                String contents = reader.lines().collect( Collectors.joining( "\n" ) );
+                contents = contents.replace( "[[[ROOT_CONTEXT]]]", rootcontext );
+                Files.write( Paths.get( destPath ), contents.getBytes( "UTF8" ) );
+            }
+        }
     }
     }
 }
 }

Some files were not shown because too many files changed in this diff