From 54f2667e457827e2dd6a4e81ef96c641f342e427 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Wed, 31 Oct 2018 15:50:05 +0100 Subject: [PATCH 01/44] openjdk 11 migration --- main/commons/pom.xml | 6 ++++++ main/pom.xml | 20 +++++++++++++++++--- main/ui/pom.xml | 10 ++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/main/commons/pom.xml b/main/commons/pom.xml index 80d96e00c..fbe878d05 100644 --- a/main/commons/pom.xml +++ b/main/commons/pom.xml @@ -11,6 +11,12 @@ Shared utilities + + + org.openjfx + javafx-controls + + com.google.guava diff --git a/main/pom.xml b/main/pom.xml index 148c9897e..e136b5c8d 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -31,6 +31,8 @@ 1.0.0 1.0.5 + 11 + 2.5 3.6 @@ -122,6 +124,18 @@ ${cryptomator.jni.version} + + + org.openjfx + javafx-controls + ${javafx.version} + + + org.openjfx + javafx-fxml + ${javafx.version} + + org.slf4j @@ -317,9 +331,9 @@ maven-compiler-plugin 3.8.0 - 10 - 10 - 10 + 11 + 11 + 11 com.google.dagger diff --git a/main/ui/pom.xml b/main/ui/pom.xml index 2421329e6..8418f0953 100644 --- a/main/ui/pom.xml +++ b/main/ui/pom.xml @@ -46,6 +46,16 @@ cryptolib + + + org.openjfx + javafx-controls + + + org.openjfx + javafx-fxml + + org.fxmisc.easybind From 744225cf7a072d85b7cfbabb56cb4bb6f288bffa Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Wed, 31 Oct 2018 15:51:00 +0100 Subject: [PATCH 02/44] changing build to jdk11 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d3492771a..1f205b58b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: java sudo: false jdk: -- openjdk10 +- openjdk11 cache: directories: - $HOME/.m2 From 26aee9e42c1be64eb0c00e18346826d079955c57 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Thu, 14 Feb 2019 18:20:11 +0100 Subject: [PATCH 03/44] create os-dependent buildkit.zip with all java resources required to build a self-contained package --- .idea/encodings.xml | 1 + .idea/misc.xml | 2 +- .travis.yml | 32 ++++- main/ant-kit/pom.xml | 99 -------------- main/ant-kit/src/main/resources/build.xml | 52 ------- .../assembly-linux.xml} | 19 +-- main/buildkit/assembly-mac.xml | 25 ++++ main/buildkit/assembly-win.xml | 25 ++++ main/buildkit/pom.xml | 129 ++++++++++++++++++ main/commons/pom.xml | 2 +- main/pom.xml | 9 +- 11 files changed, 221 insertions(+), 174 deletions(-) delete mode 100644 main/ant-kit/pom.xml delete mode 100644 main/ant-kit/src/main/resources/build.xml rename main/{ant-kit/assembly.xml => buildkit/assembly-linux.xml} (55%) create mode 100644 main/buildkit/assembly-mac.xml create mode 100644 main/buildkit/assembly-win.xml create mode 100644 main/buildkit/pom.xml diff --git a/.idea/encodings.xml b/.idea/encodings.xml index 40b0d6477..be156c7af 100644 --- a/.idea/encodings.xml +++ b/.idea/encodings.xml @@ -2,6 +2,7 @@ + diff --git a/.idea/misc.xml b/.idea/misc.xml index fd8e48573..c20d5a34e 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,7 +8,7 @@ - + \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index c04747c60..bd58d0e39 100644 --- a/.travis.yml +++ b/.travis.yml @@ -61,13 +61,39 @@ deploy: - provider: script skip_cleanup: true script: >- - curl -T main/ant-kit/target/antkit.zip + curl -T main/buildkit/target/buildkit-linux.zip -u cryptobot:${BINTRAY_API_KEY} - -H "X-Bintray-Package:ant-kit" + -H "X-Bintray-Package:buildkit" -H "X-Bintray-Version:${TRAVIS_TAG}" -H "X-Bintray-Override:1" -H "X-Bintray-Publish:1" - https://api.bintray.com/content/cryptomator/cryptomator/antkit-${TRAVIS_TAG}.zip + https://api.bintray.com/content/cryptomator/cryptomator/buildkit-linux-${TRAVIS_TAG}.zip + on: + repo: cryptomator/cryptomator + tags: true +- provider: script + skip_cleanup: true + script: >- + curl -T main/buildkit/target/buildkit-mac.zip + -u cryptobot:${BINTRAY_API_KEY} + -H "X-Bintray-Package:buildkit" + -H "X-Bintray-Version:${TRAVIS_TAG}" + -H "X-Bintray-Override:1" + -H "X-Bintray-Publish:1" + https://api.bintray.com/content/cryptomator/cryptomator/buildkit-mac-${TRAVIS_TAG}.zip + on: + repo: cryptomator/cryptomator + tags: true +- provider: script + skip_cleanup: true + script: >- + curl -T main/buildkit/target/buildkit-win.zip + -u cryptobot:${BINTRAY_API_KEY} + -H "X-Bintray-Package:buildkit" + -H "X-Bintray-Version:${TRAVIS_TAG}" + -H "X-Bintray-Override:1" + -H "X-Bintray-Publish:1" + https://api.bintray.com/content/cryptomator/cryptomator/buildkit-win-${TRAVIS_TAG}.zip on: repo: cryptomator/cryptomator tags: true diff --git a/main/ant-kit/pom.xml b/main/ant-kit/pom.xml deleted file mode 100644 index 090e5142c..000000000 --- a/main/ant-kit/pom.xml +++ /dev/null @@ -1,99 +0,0 @@ - - - 4.0.0 - - org.cryptomator - main - 1.5.0-SNAPSHOT - - ant-kit - pom - Cryptomator Ant Build Kit - Builds a package that can be built with Ant locally - - - - org.cryptomator - launcher - - - - - - - - maven-dependency-plugin - - - copy-libs - prepare-package - - copy-dependencies - - - ${project.build.directory}/libs - - - - - - - - maven-resources-plugin - 3.0.2 - - - copy-resources - prepare-package - - copy-resources - - - ${project.build.directory} - \ - UTF-8 - - - src/main/resources - true - - build.xml - - - - src/main/resources - false - - logback.xml - - - - - - - - - - - maven-assembly-plugin - 3.1.0 - - - make-assembly - package - - single - - - - - - assembly.xml - - false - antkit - - - - - \ No newline at end of file diff --git a/main/ant-kit/src/main/resources/build.xml b/main/ant-kit/src/main/resources/build.xml deleted file mode 100644 index 7ed445448..000000000 --- a/main/ant-kit/src/main/resources/build.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/main/ant-kit/assembly.xml b/main/buildkit/assembly-linux.xml similarity index 55% rename from main/ant-kit/assembly.xml rename to main/buildkit/assembly-linux.xml index 661711fe4..a26ce199b 100644 --- a/main/ant-kit/assembly.xml +++ b/main/buildkit/assembly-linux.xml @@ -15,24 +15,11 @@ libs - target/fixed-binaries - false - fixed-binaries - 755 - - - target/package - false - package - - - target + target/linux-libs - build.xml - logback.xml + *.jar - false - . + libs \ No newline at end of file diff --git a/main/buildkit/assembly-mac.xml b/main/buildkit/assembly-mac.xml new file mode 100644 index 000000000..7920af116 --- /dev/null +++ b/main/buildkit/assembly-mac.xml @@ -0,0 +1,25 @@ + + + tarball + false + + zip + + + + target/libs + + *.jar + + libs + + + target/mac-libs + + *.jar + + libs + + + \ No newline at end of file diff --git a/main/buildkit/assembly-win.xml b/main/buildkit/assembly-win.xml new file mode 100644 index 000000000..cfb0c030c --- /dev/null +++ b/main/buildkit/assembly-win.xml @@ -0,0 +1,25 @@ + + + tarball + false + + zip + + + + target/libs + + *.jar + + libs + + + target/win-libs + + *.jar + + libs + + + \ No newline at end of file diff --git a/main/buildkit/pom.xml b/main/buildkit/pom.xml new file mode 100644 index 000000000..f84838a75 --- /dev/null +++ b/main/buildkit/pom.xml @@ -0,0 +1,129 @@ + + + 4.0.0 + + org.cryptomator + main + 1.5.0-SNAPSHOT + + buildkit + pom + Cryptomator Build Kit + Builds a package that can be built with Ant locally + + + + org.cryptomator + launcher + + + + + + + + maven-dependency-plugin + 3.1.1 + + + copy-libs + prepare-package + + copy-dependencies + + + ${project.build.directory}/libs + linux,mac,win + + + + copy-linux-libs + prepare-package + + copy-dependencies + + + ${project.build.directory}/linux-libs + org.openjfx + linux + + + + copy-mac-libs + prepare-package + + copy-dependencies + + + ${project.build.directory}/mac-libs + org.openjfx + mac + + + + copy-win-libs + prepare-package + + copy-dependencies + + + ${project.build.directory}/win-libs + org.openjfx + win + + + + + + + + maven-assembly-plugin + 3.1.1 + + + assemble-linux + package + + single + + + + assembly-linux.xml + + false + buildkit-linux + + + + assemble-mac + package + + single + + + + assembly-mac.xml + + false + buildkit-mac + + + + assemble-win + package + + single + + + + assembly-win.xml + + false + buildkit-win + + + + + + + \ No newline at end of file diff --git a/main/commons/pom.xml b/main/commons/pom.xml index 6e3ddc6a3..ebb78e73f 100644 --- a/main/commons/pom.xml +++ b/main/commons/pom.xml @@ -14,7 +14,7 @@ org.openjfx - javafx-controls + javafx-base diff --git a/main/pom.xml b/main/pom.xml index d5dce5671..822b0d754 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -31,7 +31,7 @@ 1.1.3 1.0.6 - 11 + 11.0.2 2.6 3.8.1 @@ -125,6 +125,11 @@ + + org.openjfx + javafx-base + ${javafx.version} + org.openjfx javafx-controls @@ -264,7 +269,7 @@ release uber-jar - ant-kit + buildkit From 8831df9242a80c627ba523dd6d727f61d424a766 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Fri, 15 Feb 2019 14:45:12 +0100 Subject: [PATCH 04/44] Removed creation of fat jar for now. --- .idea/compiler.xml | 1 - .idea/encodings.xml | 1 - .travis.yml | 37 +++++++++++---- main/pom.xml | 1 - main/uber-jar/pom.xml | 48 -------------------- main/uber-jar/src/main/resources/logback.xml | 20 -------- 6 files changed, 28 insertions(+), 80 deletions(-) delete mode 100644 main/uber-jar/pom.xml delete mode 100644 main/uber-jar/src/main/resources/logback.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 3eb6376b7..eb6e630bd 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -26,7 +26,6 @@ - diff --git a/.idea/encodings.xml b/.idea/encodings.xml index be156c7af..0e7df1ed1 100644 --- a/.idea/encodings.xml +++ b/.idea/encodings.xml @@ -6,7 +6,6 @@ - \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index bd58d0e39..803e13215 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,27 +38,46 @@ deploy: - provider: script # SNAPSHOTS skip_cleanup: true script: >- - curl -T main/ant-kit/target/antkit.zip + curl -T main/buildkit/target/buildkit-linux.zip -u cryptobot:${BINTRAY_API_KEY} - -H "X-Bintray-Package:ant-kit" + -H "X-Bintray-Package:buildkit" -H "X-Bintray-Version:continuous" -H "X-Bintray-Override:1" -H "X-Bintray-Publish:1" - https://api.bintray.com/content/cryptomator/cryptomator/antkit-continuous.zip + https://api.bintray.com/content/cryptomator/cryptomator/buildkit-linux-continuous.zip on: repo: cryptomator/cryptomator branch: develop condition: $TRAVIS_TAG = '' -- provider: releases # RELEASE - prerelease: false - api_key: $GITHUB_API_KEY - file: - - "main/uber-jar/target/Cryptomator-$TRAVIS_TAG.jar" +- provider: script skip_cleanup: true + script: >- + curl -T main/buildkit/target/buildkit-mac.zip + -u cryptobot:${BINTRAY_API_KEY} + -H "X-Bintray-Package:buildkit" + -H "X-Bintray-Version:continuous" + -H "X-Bintray-Override:1" + -H "X-Bintray-Publish:1" + https://api.bintray.com/content/cryptomator/cryptomator/buildkit-mac-continuous.zip on: repo: cryptomator/cryptomator - tags: true + branch: develop + condition: $TRAVIS_TAG = '' - provider: script + skip_cleanup: true + script: >- + curl -T main/buildkit/target/buildkit-win.zip + -u cryptobot:${BINTRAY_API_KEY} + -H "X-Bintray-Package:buildkit" + -H "X-Bintray-Version:continuous" + -H "X-Bintray-Override:1" + -H "X-Bintray-Publish:1" + https://api.bintray.com/content/cryptomator/cryptomator/buildkit-win-continuous.zip + on: + repo: cryptomator/cryptomator + branch: develop + condition: $TRAVIS_TAG = '' +- provider: script # RELEASE skip_cleanup: true script: >- curl -T main/buildkit/target/buildkit-linux.zip diff --git a/main/pom.xml b/main/pom.xml index 822b0d754..dbd477902 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -268,7 +268,6 @@ release - uber-jar buildkit diff --git a/main/uber-jar/pom.xml b/main/uber-jar/pom.xml deleted file mode 100644 index fa28bfada..000000000 --- a/main/uber-jar/pom.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - 4.0.0 - - org.cryptomator - main - 1.5.0-SNAPSHOT - - uber-jar - Single über jar with all dependencies - - - - org.cryptomator - launcher - - - - - - - maven-shade-plugin - 3.0.0 - - - make-assembly - package - - shade - - - - - Cryptomator-${project.version} - false - - - - org.cryptomator.launcher.Cryptomator - ${project.version} - - - - - - - - \ No newline at end of file diff --git a/main/uber-jar/src/main/resources/logback.xml b/main/uber-jar/src/main/resources/logback.xml deleted file mode 100644 index 721175d1c..000000000 --- a/main/uber-jar/src/main/resources/logback.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - - - - - - From 5cb4b403cd319ed8f112e59ec4866778908d4e1d Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Fri, 15 Feb 2019 15:15:26 +0100 Subject: [PATCH 05/44] updated travis-to-bintray upload config --- .travis-deploy-release.tmpl.json | 19 +++++++ .travis-deploy-snapshot.json | 15 ++++++ .travis.yml | 86 ++++---------------------------- 3 files changed, 45 insertions(+), 75 deletions(-) create mode 100644 .travis-deploy-release.tmpl.json create mode 100644 .travis-deploy-snapshot.json diff --git a/.travis-deploy-release.tmpl.json b/.travis-deploy-release.tmpl.json new file mode 100644 index 000000000..6543a22ba --- /dev/null +++ b/.travis-deploy-release.tmpl.json @@ -0,0 +1,19 @@ +{ + "package": { + "name": "buildkit", + "repo": "cryptomator", + "subject": "cryptomator" + }, + "version": { + "name": "$TRAVIS_TAG", + "desc": "Cryptomator version $TRAVIS_TAG", + "released": "$TODAY", + "vcs_tag": "$TRAVIS_TAG", + "gpgSign": true + }, + "files": + [ + {"includePattern": "main/buildkit/target/(buildkit-[a-z]+\\.deb)", "uploadPattern": "/$1"} + ], + "publish": true +} diff --git a/.travis-deploy-snapshot.json b/.travis-deploy-snapshot.json new file mode 100644 index 000000000..9d0e80c7a --- /dev/null +++ b/.travis-deploy-snapshot.json @@ -0,0 +1,15 @@ +{ + "package": { + "name": "buildkit", + "repo": "cryptomator", + "subject": "cryptomator" + }, + "version": { + "name": "snapshot" + }, + "files": + [ + {"includePattern": "main/buildkit/target/(buildkit-[a-z]+\\.deb)", "uploadPattern": "/$1"} + ], + "publish": true +} diff --git a/.travis.yml b/.travis.yml index 803e13215..dc9e608eb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,85 +34,21 @@ before_deploy: mvn -fmain/pom.xml org.codehaus.mojo:versions-maven-plugin:set -DnewVersion=SNAPSHOT-$(echo $TRAVIS_COMMIT | head -c7) fi - mvn -fmain/pom.xml clean package -Prelease -DskipTests +- TODAY=`date +'%Y-%m-%d` envsubst '$TRAVIS_TAG $TODAY' < .travis-deploy-release.tmpl.json > .travis-deploy-release.json +- cat .travis-deploy-release.json deploy: -- provider: script # SNAPSHOTS +- provider: bintray # SNAPSHOTS + file: .travis-deploy-snapshot.json + user: cryptobot + key: $BINTRAY_API_KEY skip_cleanup: true - script: >- - curl -T main/buildkit/target/buildkit-linux.zip - -u cryptobot:${BINTRAY_API_KEY} - -H "X-Bintray-Package:buildkit" - -H "X-Bintray-Version:continuous" - -H "X-Bintray-Override:1" - -H "X-Bintray-Publish:1" - https://api.bintray.com/content/cryptomator/cryptomator/buildkit-linux-continuous.zip on: repo: cryptomator/cryptomator - branch: develop - condition: $TRAVIS_TAG = '' -- provider: script +- provider: bintray # RELEASES + file: .travis-deploy-release.json + user: cryptobot + key: $BINTRAY_API_KEY skip_cleanup: true - script: >- - curl -T main/buildkit/target/buildkit-mac.zip - -u cryptobot:${BINTRAY_API_KEY} - -H "X-Bintray-Package:buildkit" - -H "X-Bintray-Version:continuous" - -H "X-Bintray-Override:1" - -H "X-Bintray-Publish:1" - https://api.bintray.com/content/cryptomator/cryptomator/buildkit-mac-continuous.zip on: repo: cryptomator/cryptomator - branch: develop - condition: $TRAVIS_TAG = '' -- provider: script - skip_cleanup: true - script: >- - curl -T main/buildkit/target/buildkit-win.zip - -u cryptobot:${BINTRAY_API_KEY} - -H "X-Bintray-Package:buildkit" - -H "X-Bintray-Version:continuous" - -H "X-Bintray-Override:1" - -H "X-Bintray-Publish:1" - https://api.bintray.com/content/cryptomator/cryptomator/buildkit-win-continuous.zip - on: - repo: cryptomator/cryptomator - branch: develop - condition: $TRAVIS_TAG = '' -- provider: script # RELEASE - skip_cleanup: true - script: >- - curl -T main/buildkit/target/buildkit-linux.zip - -u cryptobot:${BINTRAY_API_KEY} - -H "X-Bintray-Package:buildkit" - -H "X-Bintray-Version:${TRAVIS_TAG}" - -H "X-Bintray-Override:1" - -H "X-Bintray-Publish:1" - https://api.bintray.com/content/cryptomator/cryptomator/buildkit-linux-${TRAVIS_TAG}.zip - on: - repo: cryptomator/cryptomator - tags: true -- provider: script - skip_cleanup: true - script: >- - curl -T main/buildkit/target/buildkit-mac.zip - -u cryptobot:${BINTRAY_API_KEY} - -H "X-Bintray-Package:buildkit" - -H "X-Bintray-Version:${TRAVIS_TAG}" - -H "X-Bintray-Override:1" - -H "X-Bintray-Publish:1" - https://api.bintray.com/content/cryptomator/cryptomator/buildkit-mac-${TRAVIS_TAG}.zip - on: - repo: cryptomator/cryptomator - tags: true -- provider: script - skip_cleanup: true - script: >- - curl -T main/buildkit/target/buildkit-win.zip - -u cryptobot:${BINTRAY_API_KEY} - -H "X-Bintray-Package:buildkit" - -H "X-Bintray-Version:${TRAVIS_TAG}" - -H "X-Bintray-Override:1" - -H "X-Bintray-Publish:1" - https://api.bintray.com/content/cryptomator/cryptomator/buildkit-win-${TRAVIS_TAG}.zip - on: - repo: cryptomator/cryptomator - tags: true + tags: true \ No newline at end of file From 757549919c30c86790ed06d3598f14417d82d834 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Fri, 15 Feb 2019 15:32:26 +0100 Subject: [PATCH 06/44] updated CI config --- .travis-deploy-release.tmpl.json | 2 +- .travis-deploy-snapshot.json | 2 +- .travis.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis-deploy-release.tmpl.json b/.travis-deploy-release.tmpl.json index 6543a22ba..9950bd629 100644 --- a/.travis-deploy-release.tmpl.json +++ b/.travis-deploy-release.tmpl.json @@ -13,7 +13,7 @@ }, "files": [ - {"includePattern": "main/buildkit/target/(buildkit-[a-z]+\\.deb)", "uploadPattern": "/$1"} + {"includePattern": "main/buildkit/target/(buildkit-[a-z]+\\.zip)", "uploadPattern": "/$1"} ], "publish": true } diff --git a/.travis-deploy-snapshot.json b/.travis-deploy-snapshot.json index 9d0e80c7a..28ba30f43 100644 --- a/.travis-deploy-snapshot.json +++ b/.travis-deploy-snapshot.json @@ -9,7 +9,7 @@ }, "files": [ - {"includePattern": "main/buildkit/target/(buildkit-[a-z]+\\.deb)", "uploadPattern": "/$1"} + {"includePattern": "main/buildkit/target/(buildkit-[a-z]+\\.zip)", "uploadPattern": "/$1"} ], "publish": true } diff --git a/.travis.yml b/.travis.yml index d42ec896f..1621e329f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,7 +34,7 @@ before_deploy: mvn -fmain/pom.xml org.codehaus.mojo:versions-maven-plugin:set -DnewVersion=SNAPSHOT-$(echo $TRAVIS_COMMIT | head -c7) fi - mvn -fmain/pom.xml clean package -Prelease -DskipTests -- TODAY=`date +'%Y-%m-%d` envsubst '$TRAVIS_TAG $TODAY' < .travis-deploy-release.tmpl.json > .travis-deploy-release.json +- TODAY=`date +'%Y-%m-%d'`; envsubst '$TRAVIS_TAG $TODAY' < .travis-deploy-release.tmpl.json > .travis-deploy-release.json - cat .travis-deploy-release.json deploy: - provider: bintray # SNAPSHOTS From e1930505d1dba409cc20b2422b50cb5f48ebacea Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Fri, 15 Feb 2019 15:46:44 +0100 Subject: [PATCH 07/44] updated build config --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1621e329f..7a88859de 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,7 +34,7 @@ before_deploy: mvn -fmain/pom.xml org.codehaus.mojo:versions-maven-plugin:set -DnewVersion=SNAPSHOT-$(echo $TRAVIS_COMMIT | head -c7) fi - mvn -fmain/pom.xml clean package -Prelease -DskipTests -- TODAY=`date +'%Y-%m-%d'`; envsubst '$TRAVIS_TAG $TODAY' < .travis-deploy-release.tmpl.json > .travis-deploy-release.json +- export TODAY=`date +'%Y-%m-%d'`; envsubst '$TRAVIS_TAG $TODAY' < .travis-deploy-release.tmpl.json > .travis-deploy-release.json - cat .travis-deploy-release.json deploy: - provider: bintray # SNAPSHOTS From f16c3d5110a76b0fd64ff3aac0f8383bb09407c4 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Fri, 15 Feb 2019 16:00:59 +0100 Subject: [PATCH 08/44] updated build config --- .travis-deploy-release.tmpl.json | 2 +- .travis-deploy-snapshot.json | 2 +- .travis.yml | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis-deploy-release.tmpl.json b/.travis-deploy-release.tmpl.json index 9950bd629..435e3dd82 100644 --- a/.travis-deploy-release.tmpl.json +++ b/.travis-deploy-release.tmpl.json @@ -13,7 +13,7 @@ }, "files": [ - {"includePattern": "main/buildkit/target/(buildkit-[a-z]+\\.zip)", "uploadPattern": "/$1"} + {"includePattern": "main/buildkit/target/(buildkit-[a-z]+\\.zip)", "uploadPattern": "/$TRAVIS_TAG/$1"} ], "publish": true } diff --git a/.travis-deploy-snapshot.json b/.travis-deploy-snapshot.json index 28ba30f43..fa3897a4d 100644 --- a/.travis-deploy-snapshot.json +++ b/.travis-deploy-snapshot.json @@ -9,7 +9,7 @@ }, "files": [ - {"includePattern": "main/buildkit/target/(buildkit-[a-z]+\\.zip)", "uploadPattern": "/$1"} + {"includePattern": "main/buildkit/target/(buildkit-[a-z]+\\.zip)", "uploadPattern": "/snapshot/$1", "matrixParams": {"override": 1}} ], "publish": true } diff --git a/.travis.yml b/.travis.yml index 7a88859de..05b105162 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,7 +35,6 @@ before_deploy: fi - mvn -fmain/pom.xml clean package -Prelease -DskipTests - export TODAY=`date +'%Y-%m-%d'`; envsubst '$TRAVIS_TAG $TODAY' < .travis-deploy-release.tmpl.json > .travis-deploy-release.json -- cat .travis-deploy-release.json deploy: - provider: bintray # SNAPSHOTS file: .travis-deploy-snapshot.json From 0caa9988d30736319a39aa3ded358e52c8923f1d Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Fri, 15 Feb 2019 19:38:21 +0100 Subject: [PATCH 09/44] add version.txt to buildkit --- main/buildkit/assembly-linux.xml | 7 +++++ main/buildkit/assembly-mac.xml | 7 +++++ main/buildkit/assembly-win.xml | 7 +++++ main/buildkit/pom.xml | 30 +++++++++++++++++++- main/buildkit/src/main/resources/version.txt | 1 + 5 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 main/buildkit/src/main/resources/version.txt diff --git a/main/buildkit/assembly-linux.xml b/main/buildkit/assembly-linux.xml index a26ce199b..9784212b5 100644 --- a/main/buildkit/assembly-linux.xml +++ b/main/buildkit/assembly-linux.xml @@ -7,6 +7,13 @@ zip + + target/ + + version.txt + + libs + target/libs diff --git a/main/buildkit/assembly-mac.xml b/main/buildkit/assembly-mac.xml index 7920af116..1b4e94ef9 100644 --- a/main/buildkit/assembly-mac.xml +++ b/main/buildkit/assembly-mac.xml @@ -7,6 +7,13 @@ zip + + target/ + + version.txt + + libs + target/libs diff --git a/main/buildkit/assembly-win.xml b/main/buildkit/assembly-win.xml index cfb0c030c..982ff6037 100644 --- a/main/buildkit/assembly-win.xml +++ b/main/buildkit/assembly-win.xml @@ -7,6 +7,13 @@ zip + + target/ + + version.txt + + libs + target/libs diff --git a/main/buildkit/pom.xml b/main/buildkit/pom.xml index f84838a75..32fa59d67 100644 --- a/main/buildkit/pom.xml +++ b/main/buildkit/pom.xml @@ -18,8 +18,36 @@ - + + + + org.apache.maven.plugins + maven-resources-plugin + 3.1.0 + + + copy-resources + prepare-package + + copy-resources + + + ${project.build.directory} + + + ${project.basedir}/src/main/resources + + version.txt + + true + + + + + + + maven-dependency-plugin diff --git a/main/buildkit/src/main/resources/version.txt b/main/buildkit/src/main/resources/version.txt new file mode 100644 index 000000000..f2ab45c3b --- /dev/null +++ b/main/buildkit/src/main/resources/version.txt @@ -0,0 +1 @@ +${project.version} \ No newline at end of file From 1e80f4bba4485e56da369bbf71a735b4c45bd891 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 18 Feb 2019 15:28:11 +0100 Subject: [PATCH 10/44] Upgrade to JUnit 5 --- .../common/SemVerComparatorTest.java | 46 ++++++------- .../settings/SettingsJsonAdapterTest.java | 18 +++--- .../common/settings/SettingsTest.java | 6 +- .../VaultSettingsJsonAdapterTest.java | 19 +++--- .../common/settings/VaultSettingsTest.java | 4 +- .../keychain/KeychainModuleTest.java | 12 ++-- .../WindowsProtectedKeychainAccessTest.java | 37 +++++------ .../launcher/FileOpenRequestHandlerTest.java | 12 ++-- .../InterProcessCommunicatorTest.java | 28 ++++---- .../LaunchBasedTriggeringPolicyTest.java | 14 ++-- main/pom.xml | 40 ++++-------- .../cryptomator/ui/l10n/LocalizationTest.java | 12 ++-- .../ui/model/UpgradeVersion3to4Test.java | 64 +++++++++---------- .../ui/util/PasswordStrengthUtilTest.java | 12 ++-- 14 files changed, 154 insertions(+), 170 deletions(-) diff --git a/main/commons/src/test/java/org/cryptomator/common/SemVerComparatorTest.java b/main/commons/src/test/java/org/cryptomator/common/SemVerComparatorTest.java index cf22dcecc..52a9366c9 100644 --- a/main/commons/src/test/java/org/cryptomator/common/SemVerComparatorTest.java +++ b/main/commons/src/test/java/org/cryptomator/common/SemVerComparatorTest.java @@ -8,10 +8,10 @@ *******************************************************************************/ package org.cryptomator.common; -import java.util.Comparator; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; -import org.junit.Assert; -import org.junit.Test; +import java.util.Comparator; public class SemVerComparatorTest { @@ -21,38 +21,38 @@ public class SemVerComparatorTest { @Test public void compareEqualVersions() { - Assert.assertEquals(0, Integer.signum(semVerComparator.compare("1.23.4", "1.23.4"))); - Assert.assertEquals(0, Integer.signum(semVerComparator.compare("1.23.4-alpha", "1.23.4-alpha"))); - Assert.assertEquals(0, Integer.signum(semVerComparator.compare("1.23.4+20170101", "1.23.4+20171231"))); - Assert.assertEquals(0, Integer.signum(semVerComparator.compare("1.23.4-alpha+20170101", "1.23.4-alpha+20171231"))); + Assertions.assertEquals(0, Integer.signum(semVerComparator.compare("1.23.4", "1.23.4"))); + Assertions.assertEquals(0, Integer.signum(semVerComparator.compare("1.23.4-alpha", "1.23.4-alpha"))); + Assertions.assertEquals(0, Integer.signum(semVerComparator.compare("1.23.4+20170101", "1.23.4+20171231"))); + Assertions.assertEquals(0, Integer.signum(semVerComparator.compare("1.23.4-alpha+20170101", "1.23.4-alpha+20171231"))); } // newer versions in first argument @Test public void compareHigherToLowerVersions() { - Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.5", "1.23.4"))); - Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.24.4", "1.23.4"))); - Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4", "1.23"))); - Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4", "1.23.4-SNAPSHOT"))); - Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4", "1.23.4-56.78"))); - Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4-beta", "1.23.4-alpha"))); - Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4-alpha.1", "1.23.4-alpha"))); - Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4-56.79", "1.23.4-56.78"))); + Assertions.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.5", "1.23.4"))); + Assertions.assertEquals(1, Integer.signum(semVerComparator.compare("1.24.4", "1.23.4"))); + Assertions.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4", "1.23"))); + Assertions.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4", "1.23.4-SNAPSHOT"))); + Assertions.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4", "1.23.4-56.78"))); + Assertions.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4-beta", "1.23.4-alpha"))); + Assertions.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4-alpha.1", "1.23.4-alpha"))); + Assertions.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4-56.79", "1.23.4-56.78"))); } // newer versions in second argument @Test public void compareLowerToHigherVersions() { - Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4", "1.23.5"))); - Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4", "1.24.4"))); - Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23", "1.23.4"))); - Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-SNAPSHOT", "1.23.4"))); - Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-56.78", "1.23.4"))); - Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-alpha", "1.23.4-beta"))); - Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-alpha", "1.23.4-alpha.1"))); - Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-56.78", "1.23.4-56.79"))); + Assertions.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4", "1.23.5"))); + Assertions.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4", "1.24.4"))); + Assertions.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23", "1.23.4"))); + Assertions.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-SNAPSHOT", "1.23.4"))); + Assertions.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-56.78", "1.23.4"))); + Assertions.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-alpha", "1.23.4-beta"))); + Assertions.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-alpha", "1.23.4-alpha.1"))); + Assertions.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-56.78", "1.23.4-56.79"))); } } diff --git a/main/commons/src/test/java/org/cryptomator/common/settings/SettingsJsonAdapterTest.java b/main/commons/src/test/java/org/cryptomator/common/settings/SettingsJsonAdapterTest.java index cee09295a..2d0e31d2a 100644 --- a/main/commons/src/test/java/org/cryptomator/common/settings/SettingsJsonAdapterTest.java +++ b/main/commons/src/test/java/org/cryptomator/common/settings/SettingsJsonAdapterTest.java @@ -5,10 +5,10 @@ *******************************************************************************/ package org.cryptomator.common.settings; -import java.io.IOException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; -import org.junit.Assert; -import org.junit.Test; +import java.io.IOException; public class SettingsJsonAdapterTest { @@ -26,12 +26,12 @@ public class SettingsJsonAdapterTest { Settings settings = adapter.fromJson(json); - Assert.assertTrue(settings.checkForUpdates().get()); - Assert.assertEquals(2, settings.getDirectories().size()); - Assert.assertEquals(8080, settings.port().get()); - Assert.assertEquals(42, settings.numTrayNotifications().get()); - Assert.assertEquals("dav", settings.preferredGvfsScheme().get()); - Assert.assertEquals(VolumeImpl.FUSE, settings.preferredVolumeImpl().get()); + Assertions.assertTrue(settings.checkForUpdates().get()); + Assertions.assertEquals(2, settings.getDirectories().size()); + Assertions.assertEquals(8080, settings.port().get()); + Assertions.assertEquals(42, settings.numTrayNotifications().get()); + Assertions.assertEquals("dav", settings.preferredGvfsScheme().get()); + Assertions.assertEquals(VolumeImpl.FUSE, settings.preferredVolumeImpl().get()); } } diff --git a/main/commons/src/test/java/org/cryptomator/common/settings/SettingsTest.java b/main/commons/src/test/java/org/cryptomator/common/settings/SettingsTest.java index 320d0504c..6aadb27b3 100644 --- a/main/commons/src/test/java/org/cryptomator/common/settings/SettingsTest.java +++ b/main/commons/src/test/java/org/cryptomator/common/settings/SettingsTest.java @@ -5,12 +5,12 @@ *******************************************************************************/ package org.cryptomator.common.settings; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + import java.io.IOException; import java.util.function.Consumer; -import org.junit.Test; -import org.mockito.Mockito; - public class SettingsTest { @Test diff --git a/main/commons/src/test/java/org/cryptomator/common/settings/VaultSettingsJsonAdapterTest.java b/main/commons/src/test/java/org/cryptomator/common/settings/VaultSettingsJsonAdapterTest.java index e3f31251e..32f393842 100644 --- a/main/commons/src/test/java/org/cryptomator/common/settings/VaultSettingsJsonAdapterTest.java +++ b/main/commons/src/test/java/org/cryptomator/common/settings/VaultSettingsJsonAdapterTest.java @@ -5,15 +5,14 @@ *******************************************************************************/ package org.cryptomator.common.settings; +import com.google.gson.stream.JsonReader; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + import java.io.IOException; import java.io.StringReader; import java.nio.file.Paths; -import org.junit.Assert; -import org.junit.Test; - -import com.google.gson.stream.JsonReader; - public class VaultSettingsJsonAdapterTest { private final VaultSettingsJsonAdapter adapter = new VaultSettingsJsonAdapter(); @@ -24,11 +23,11 @@ public class VaultSettingsJsonAdapterTest { JsonReader jsonReader = new JsonReader(new StringReader(json)); VaultSettings vaultSettings = adapter.read(jsonReader); - Assert.assertEquals("foo", vaultSettings.getId()); - Assert.assertEquals(Paths.get("/foo/bar"), vaultSettings.path().get()); - Assert.assertEquals("test", vaultSettings.mountName().get()); - Assert.assertEquals("X", vaultSettings.winDriveLetter().get()); - Assert.assertEquals("/home/test/crypto", vaultSettings.individualMountPath().get()); + Assertions.assertEquals("foo", vaultSettings.getId()); + Assertions.assertEquals(Paths.get("/foo/bar"), vaultSettings.path().get()); + Assertions.assertEquals("test", vaultSettings.mountName().get()); + Assertions.assertEquals("X", vaultSettings.winDriveLetter().get()); + Assertions.assertEquals("/home/test/crypto", vaultSettings.individualMountPath().get()); } } diff --git a/main/commons/src/test/java/org/cryptomator/common/settings/VaultSettingsTest.java b/main/commons/src/test/java/org/cryptomator/common/settings/VaultSettingsTest.java index 9aeac9b7a..042ff9896 100644 --- a/main/commons/src/test/java/org/cryptomator/common/settings/VaultSettingsTest.java +++ b/main/commons/src/test/java/org/cryptomator/common/settings/VaultSettingsTest.java @@ -8,9 +8,9 @@ *******************************************************************************/ package org.cryptomator.common.settings; -import static org.junit.Assert.assertEquals; +import org.junit.jupiter.api.Test; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; public class VaultSettingsTest { diff --git a/main/keychain/src/test/java/org/cryptomator/keychain/KeychainModuleTest.java b/main/keychain/src/test/java/org/cryptomator/keychain/KeychainModuleTest.java index 1bd556061..c177060a3 100644 --- a/main/keychain/src/test/java/org/cryptomator/keychain/KeychainModuleTest.java +++ b/main/keychain/src/test/java/org/cryptomator/keychain/KeychainModuleTest.java @@ -5,20 +5,20 @@ *******************************************************************************/ package org.cryptomator.keychain; -import java.util.Optional; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; -import org.junit.Assert; -import org.junit.Test; +import java.util.Optional; public class KeychainModuleTest { @Test public void testGetKeychain() { Optional keychainAccess = DaggerTestKeychainComponent.builder().keychainModule(new TestKeychainModule()).build().keychainAccess(); - Assert.assertTrue(keychainAccess.isPresent()); - Assert.assertTrue(keychainAccess.get() instanceof MapKeychainAccess); + Assertions.assertTrue(keychainAccess.isPresent()); + Assertions.assertTrue(keychainAccess.get() instanceof MapKeychainAccess); keychainAccess.get().storePassphrase("test", "asd"); - Assert.assertArrayEquals("asd".toCharArray(), keychainAccess.get().loadPassphrase("test")); + Assertions.assertArrayEquals("asd".toCharArray(), keychainAccess.get().loadPassphrase("test")); } } diff --git a/main/keychain/src/test/java/org/cryptomator/keychain/WindowsProtectedKeychainAccessTest.java b/main/keychain/src/test/java/org/cryptomator/keychain/WindowsProtectedKeychainAccessTest.java index c058baadc..88c23ffa1 100644 --- a/main/keychain/src/test/java/org/cryptomator/keychain/WindowsProtectedKeychainAccessTest.java +++ b/main/keychain/src/test/java/org/cryptomator/keychain/WindowsProtectedKeychainAccessTest.java @@ -5,32 +5,27 @@ *******************************************************************************/ package org.cryptomator.keychain; +import org.cryptomator.jni.WinDataProtection; +import org.cryptomator.jni.WinFunctions; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.mockito.stubbing.Answer; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Optional; -import org.cryptomator.jni.WinDataProtection; -import org.cryptomator.jni.WinFunctions; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.mockito.Mockito; -import org.mockito.stubbing.Answer; - public class WindowsProtectedKeychainAccessTest { - @Rule - public final ExpectedException thrown = ExpectedException.none(); - private Path tmpFile; private WindowsProtectedKeychainAccess keychain; - @Before - public void setup() throws IOException, ReflectiveOperationException { + @BeforeEach + public void setup() throws IOException { tmpFile = Files.createTempFile("unit-tests", ".tmp"); System.setProperty("cryptomator.keychainPath", tmpFile.toAbsolutePath().normalize().toString()); WinFunctions winFunctions = Mockito.mock(WinFunctions.class); @@ -42,7 +37,7 @@ public class WindowsProtectedKeychainAccessTest { keychain = new WindowsProtectedKeychainAccess(Optional.of(winFunctions)); } - @After + @AfterEach public void teardown() throws IOException { Files.deleteIfExists(tmpFile); } @@ -55,11 +50,11 @@ public class WindowsProtectedKeychainAccessTest { keychain.storePassphrase("myOtherPassword", storedPw2); String loadedPw1 = new String(keychain.loadPassphrase("myPassword")); String loadedPw2 = new String(keychain.loadPassphrase("myOtherPassword")); - Assert.assertEquals(storedPw1, loadedPw1); - Assert.assertEquals(storedPw2, loadedPw2); + Assertions.assertEquals(storedPw1, loadedPw1); + Assertions.assertEquals(storedPw2, loadedPw2); keychain.deletePassphrase("myPassword"); - Assert.assertNull(keychain.loadPassphrase("myPassword")); - Assert.assertNull(keychain.loadPassphrase("nonExistingPassword")); + Assertions.assertNull(keychain.loadPassphrase("myPassword")); + Assertions.assertNull(keychain.loadPassphrase("nonExistingPassword")); } } diff --git a/main/launcher/src/test/java/org/cryptomator/launcher/FileOpenRequestHandlerTest.java b/main/launcher/src/test/java/org/cryptomator/launcher/FileOpenRequestHandlerTest.java index 54acb065c..9bf8cd2be 100644 --- a/main/launcher/src/test/java/org/cryptomator/launcher/FileOpenRequestHandlerTest.java +++ b/main/launcher/src/test/java/org/cryptomator/launcher/FileOpenRequestHandlerTest.java @@ -5,6 +5,10 @@ *******************************************************************************/ package org.cryptomator.launcher; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + import java.io.IOException; import java.nio.file.FileSystem; import java.nio.file.InvalidPathException; @@ -14,10 +18,6 @@ import java.nio.file.spi.FileSystemProvider; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - public class FileOpenRequestHandlerTest { @Test @@ -37,8 +37,8 @@ public class FileOpenRequestHandlerTest { FileOpenRequestHandler handler = new FileOpenRequestHandler(queue); handler.handleLaunchArgs(fs, new String[] {"foo", "bar"}); - Assert.assertEquals(p1, queue.poll()); - Assert.assertEquals(p2, queue.poll()); + Assertions.assertEquals(p1, queue.poll()); + Assertions.assertEquals(p2, queue.poll()); } @Test diff --git a/main/launcher/src/test/java/org/cryptomator/launcher/InterProcessCommunicatorTest.java b/main/launcher/src/test/java/org/cryptomator/launcher/InterProcessCommunicatorTest.java index 81c62aca3..2b7d553b9 100644 --- a/main/launcher/src/test/java/org/cryptomator/launcher/InterProcessCommunicatorTest.java +++ b/main/launcher/src/test/java/org/cryptomator/launcher/InterProcessCommunicatorTest.java @@ -5,6 +5,11 @@ *******************************************************************************/ package org.cryptomator.launcher; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SeekableByteChannel; @@ -15,11 +20,6 @@ import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.spi.FileSystemProvider; import java.util.concurrent.atomic.AtomicInteger; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - public class InterProcessCommunicatorTest { Path portFilePath = Mockito.mock(Path.class); @@ -30,7 +30,7 @@ public class InterProcessCommunicatorTest { SeekableByteChannel portFileChannel = Mockito.mock(SeekableByteChannel.class); AtomicInteger port = new AtomicInteger(-1); - @Before + @BeforeEach public void setup() throws IOException { Mockito.when(portFilePath.getFileSystem()).thenReturn(fs); Mockito.when(portFilePath.toAbsolutePath()).thenReturn(portFilePath); @@ -53,15 +53,17 @@ public class InterProcessCommunicatorTest { }); } - @Test(expected = UnsupportedOperationException.class) + @Test public void testStartWithDummyPort1() throws IOException { port.set(0); InterProcessCommunicationProtocol protocol = Mockito.mock(InterProcessCommunicationProtocol.class); try (InterProcessCommunicator result = InterProcessCommunicator.start(portFilePath, protocol)) { - Assert.assertTrue(result.isServer()); + Assertions.assertTrue(result.isServer()); Mockito.verify(provider).createDirectory(portFileParentPath); Mockito.verifyZeroInteractions(protocol); - result.handleLaunchArgs(new String[] {"foo"}); + Assertions.assertThrows(UnsupportedOperationException.class, () -> { + result.handleLaunchArgs(new String[] {"foo"}); + }); } } @@ -71,7 +73,7 @@ public class InterProcessCommunicatorTest { InterProcessCommunicationProtocol protocol = Mockito.mock(InterProcessCommunicationProtocol.class); try (InterProcessCommunicator result = InterProcessCommunicator.start(portFilePath, protocol)) { - Assert.assertTrue(result.isServer()); + Assertions.assertTrue(result.isServer()); Mockito.verify(provider).createDirectory(portFileParentPath); Mockito.verifyZeroInteractions(protocol); } @@ -82,14 +84,14 @@ public class InterProcessCommunicatorTest { port.set(-1); InterProcessCommunicationProtocol protocol = Mockito.mock(InterProcessCommunicationProtocol.class); try (InterProcessCommunicator result1 = InterProcessCommunicator.start(portFilePath, protocol)) { - Assert.assertTrue(result1.isServer()); + Assertions.assertTrue(result1.isServer()); Mockito.verify(provider, Mockito.times(1)).createDirectory(portFileParentPath); Mockito.verifyZeroInteractions(protocol); try (InterProcessCommunicator result2 = InterProcessCommunicator.start(portFilePath, null)) { - Assert.assertFalse(result2.isServer()); + Assertions.assertFalse(result2.isServer()); Mockito.verify(provider, Mockito.times(1)).createDirectory(portFileParentPath); - Assert.assertNotSame(result1, result2); + Assertions.assertNotSame(result1, result2); result2.handleLaunchArgs(new String[] {"foo"}); Mockito.verify(protocol).handleLaunchArgs(new String[] {"foo"}); diff --git a/main/launcher/src/test/java/org/cryptomator/logging/LaunchBasedTriggeringPolicyTest.java b/main/launcher/src/test/java/org/cryptomator/logging/LaunchBasedTriggeringPolicyTest.java index 2896398ab..16385d949 100644 --- a/main/launcher/src/test/java/org/cryptomator/logging/LaunchBasedTriggeringPolicyTest.java +++ b/main/launcher/src/test/java/org/cryptomator/logging/LaunchBasedTriggeringPolicyTest.java @@ -5,12 +5,12 @@ *******************************************************************************/ package org.cryptomator.logging; -import java.io.File; - -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import java.io.File; + public class LaunchBasedTriggeringPolicyTest { @Test @@ -21,15 +21,15 @@ public class LaunchBasedTriggeringPolicyTest { // 1st invocation boolean triggered = policy.isTriggeringEvent(activeFile, event); - Assert.assertTrue(triggered); + Assertions.assertTrue(triggered); // 2nd invocation triggered = policy.isTriggeringEvent(activeFile, event); - Assert.assertFalse(triggered); + Assertions.assertFalse(triggered); // 3rd invocation triggered = policy.isTriggeringEvent(activeFile, event); - Assert.assertFalse(triggered); + Assertions.assertFalse(triggered); Mockito.verifyZeroInteractions(activeFile); Mockito.verifyZeroInteractions(event); diff --git a/main/pom.xml b/main/pom.xml index a03e4e420..add0f6161 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -45,10 +45,9 @@ 1.7.25 1.2.3 - 4.12 - 4.12.1 - 2.23.0 - 1.3 + 5.4.0 + 2.24.0 + 1.3 @@ -201,20 +200,10 @@ - junit - junit - ${junit.version} - - - hamcrest-core - org.hamcrest - - - - - de.bechte.junit - junit-hierarchicalcontextrunner - ${junit.hierarchicalrunner.version} + org.junit.jupiter + junit-jupiter + ${junit.jupiter.version} + test org.mockito @@ -236,9 +225,8 @@ slf4j-api - junit - junit - test + org.junit.jupiter + junit-jupiter org.hamcrest @@ -250,11 +238,6 @@ mockito-core test - - de.bechte.junit - junit-hierarchicalcontextrunner - test - @@ -345,6 +328,11 @@ + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.1 + diff --git a/main/ui/src/test/java/org/cryptomator/ui/l10n/LocalizationTest.java b/main/ui/src/test/java/org/cryptomator/ui/l10n/LocalizationTest.java index 9644d103f..24609365b 100644 --- a/main/ui/src/test/java/org/cryptomator/ui/l10n/LocalizationTest.java +++ b/main/ui/src/test/java/org/cryptomator/ui/l10n/LocalizationTest.java @@ -8,6 +8,11 @@ *******************************************************************************/ package org.cryptomator.ui.l10n; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -21,11 +26,6 @@ import java.util.ResourceBundle; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.junit.Assert; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class LocalizationTest { private static final Logger LOG = LoggerFactory.getLogger(LocalizationTest.class); @@ -54,7 +54,7 @@ public class LocalizationTest { ResourceBundle lang = loadLanguage(RESOURCE_FOLDER_PATH + langFileName); allGood &= allStringFormatSpecifiersMatchReferenceLanguage(ref, lang, langFileName); } - Assert.assertTrue(allGood); + Assertions.assertTrue(allGood); } private boolean allStringFormatSpecifiersMatchReferenceLanguage(ResourceBundle ref, ResourceBundle lang, String langFileName) { diff --git a/main/ui/src/test/java/org/cryptomator/ui/model/UpgradeVersion3to4Test.java b/main/ui/src/test/java/org/cryptomator/ui/model/UpgradeVersion3to4Test.java index 8b1f18e88..b1d430ceb 100644 --- a/main/ui/src/test/java/org/cryptomator/ui/model/UpgradeVersion3to4Test.java +++ b/main/ui/src/test/java/org/cryptomator/ui/model/UpgradeVersion3to4Test.java @@ -1,25 +1,22 @@ package org.cryptomator.ui.model; +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; +import org.cryptomator.ui.l10n.Localization; +import org.cryptomator.ui.l10n.LocalizationMock; +import org.cryptomator.ui.model.UpgradeStrategy.UpgradeFailedException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; -import org.cryptomator.ui.l10n.Localization; -import org.cryptomator.ui.l10n.LocalizationMock; -import org.cryptomator.ui.model.UpgradeStrategy.UpgradeFailedException; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.mockito.Mockito; - -import com.google.common.jimfs.Configuration; -import com.google.common.jimfs.Jimfs; - public class UpgradeVersion3to4Test { private static final Localization L10N = new LocalizationMock(); @@ -33,8 +30,6 @@ public class UpgradeVersion3to4Test { + " \"versionMac\": \"iUmRRHITuyJsJbVNqGNw+82YQ4A3Rma7j/y1v0DCVLA=\"" // + "}"; - @Rule - public final ExpectedException thrown = ExpectedException.none(); private final UpgradeStrategy upgradeStrategy = new UpgradeVersion3to4(L10N); private FileSystem fs; private Path fsRoot; @@ -42,7 +37,7 @@ public class UpgradeVersion3to4Test { private Path dataDir; private Path metadataDir; - @Before + @BeforeEach public void setup() throws IOException { fs = Jimfs.newFileSystem(Configuration.unix()); fsRoot = fs.getPath("/"); @@ -54,22 +49,23 @@ public class UpgradeVersion3to4Test { Files.write(fsRoot.resolve("masterkey.cryptomator"), NULL_KEY_CONTENTS.getBytes(StandardCharsets.US_ASCII)); } - @After + @AfterEach public void teardown() throws IOException { fs.close(); } @Test public void upgradeFailsWithWrongPassword() throws UpgradeFailedException { - thrown.expect(UpgradeFailedException.class); - thrown.expectMessage("unlock.errorMessage.wrongPassword"); - upgradeStrategy.upgrade(vault, "asdd"); + UpgradeFailedException e = Assertions.assertThrows(UpgradeFailedException.class, () -> { + upgradeStrategy.upgrade(vault, "asdd"); + }); + Assertions.assertEquals("unlock.errorMessage.wrongPassword", e.getMessage()); } @Test public void upgradeCreatesBackup() throws UpgradeFailedException { upgradeStrategy.upgrade(vault, "asd"); - Assert.assertTrue(Files.exists(fsRoot.resolve("masterkey.cryptomator.bkup"))); + Assertions.assertTrue(Files.exists(fsRoot.resolve("masterkey.cryptomator.bkup"))); } @Test @@ -81,8 +77,8 @@ public class UpgradeVersion3to4Test { upgradeStrategy.upgrade(vault, "asd"); Path newFile = lvl2Dir.resolve("0ABCDEFGH"); - Assert.assertTrue(Files.exists(newFile)); - Assert.assertTrue(Files.notExists(oldFile)); + Assertions.assertTrue(Files.exists(newFile)); + Assertions.assertTrue(Files.notExists(oldFile)); } @Test @@ -94,8 +90,8 @@ public class UpgradeVersion3to4Test { upgradeStrategy.upgrade(vault, "asd"); Path newFile = lvl2Dir.resolve("0ABCDEFGH (1)"); - Assert.assertTrue(Files.exists(newFile)); - Assert.assertTrue(Files.notExists(oldFile)); + Assertions.assertTrue(Files.exists(newFile)); + Assertions.assertTrue(Files.notExists(oldFile)); } @Test @@ -106,7 +102,7 @@ public class UpgradeVersion3to4Test { Files.createFile(oldFile); upgradeStrategy.upgrade(vault, "asd"); - Assert.assertTrue(Files.exists(oldFile)); + Assertions.assertTrue(Files.exists(oldFile)); } @Test @@ -123,9 +119,9 @@ public class UpgradeVersion3to4Test { // hex2base32(sha1("0OPQRSTUVWXYZ====")) = DDLCFQ3ODTEAHEZJPHIJQRDHROB3K42G Path newMetadataFile = metadataDir.resolve("DD/LC/DDLCFQ3ODTEAHEZJPHIJQRDHROB3K42G.lng"); Path newFile = lvl2Dir.resolve("DDLCFQ3ODTEAHEZJPHIJQRDHROB3K42G.lng"); - Assert.assertTrue(Files.exists(newFile)); - Assert.assertTrue(Files.exists(newMetadataFile)); - Assert.assertTrue(Files.notExists(oldFile)); + Assertions.assertTrue(Files.exists(newFile)); + Assertions.assertTrue(Files.exists(newMetadataFile)); + Assertions.assertTrue(Files.notExists(oldFile)); } @Test @@ -142,9 +138,9 @@ public class UpgradeVersion3to4Test { // hex2base32(sha1("0OPQRSTUVWXYZ====")) = DDLCFQ3ODTEAHEZJPHIJQRDHROB3K42G Path newMetadataFile = metadataDir.resolve("DD/LC/DDLCFQ3ODTEAHEZJPHIJQRDHROB3K42G.lng"); Path newFile = lvl2Dir.resolve("DDLCFQ3ODTEAHEZJPHIJQRDHROB3K42G (1).lng"); - Assert.assertTrue(Files.exists(newFile)); - Assert.assertTrue(Files.exists(newMetadataFile)); - Assert.assertTrue(Files.notExists(oldFile)); + Assertions.assertTrue(Files.exists(newFile)); + Assertions.assertTrue(Files.exists(newMetadataFile)); + Assertions.assertTrue(Files.notExists(oldFile)); } @Test @@ -158,7 +154,7 @@ public class UpgradeVersion3to4Test { Files.write(oldMetadataFile, "OPQRSTUVWXYZ====".getBytes(StandardCharsets.UTF_8)); upgradeStrategy.upgrade(vault, "asd"); - Assert.assertTrue(Files.exists(oldFile)); + Assertions.assertTrue(Files.exists(oldFile)); } } diff --git a/main/ui/src/test/java/org/cryptomator/ui/util/PasswordStrengthUtilTest.java b/main/ui/src/test/java/org/cryptomator/ui/util/PasswordStrengthUtilTest.java index 2a9411077..553d54027 100644 --- a/main/ui/src/test/java/org/cryptomator/ui/util/PasswordStrengthUtilTest.java +++ b/main/ui/src/test/java/org/cryptomator/ui/util/PasswordStrengthUtilTest.java @@ -1,20 +1,24 @@ package org.cryptomator.ui.util; import org.cryptomator.ui.l10n.Localization; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import java.time.Duration; + public class PasswordStrengthUtilTest { - @Test(timeout = 5000) + @Test public void testLongPasswords() { PasswordStrengthUtil util = new PasswordStrengthUtil(Mockito.mock(Localization.class)); StringBuilder longPwBuilder = new StringBuilder(); for (int i = 0; i < 10000; i++) { longPwBuilder.append('x'); } - util.computeRate(longPwBuilder.toString()); + Assertions.assertTimeout(Duration.ofSeconds(5), () -> { + util.computeRate(longPwBuilder.toString()); + }); } } From d7dda7d24902a9df864a8753a2856222db61b408 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 18 Feb 2019 16:53:41 +0100 Subject: [PATCH 11/44] Preparations for allowing to specify multiple paths to which Cryptomator writes settings, logs, etc. see #710 --- .../org/cryptomator/common/Environment.java | 73 ++++++++++++ .../common/settings/SettingsProvider.java | 75 +++++------- .../cryptomator/common/EnvironmentTest.java | 110 ++++++++++++++++++ 3 files changed, 213 insertions(+), 45 deletions(-) create mode 100644 main/commons/src/main/java/org/cryptomator/common/Environment.java create mode 100644 main/commons/src/test/java/org/cryptomator/common/EnvironmentTest.java diff --git a/main/commons/src/main/java/org/cryptomator/common/Environment.java b/main/commons/src/main/java/org/cryptomator/common/Environment.java new file mode 100644 index 000000000..e094f4257 --- /dev/null +++ b/main/commons/src/main/java/org/cryptomator/common/Environment.java @@ -0,0 +1,73 @@ +package org.cryptomator.common; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Splitter; +import com.google.common.base.Strings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.Predicate; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +@Singleton +public class Environment { + + private static final Logger LOG = LoggerFactory.getLogger(Environment.class); + private static final String USER_HOME = System.getProperty("user.home"); + private static final Path RELATIVE_HOME_DIR = Paths.get("~"); + private static final Path ABSOLUTE_HOME_DIR = Paths.get(USER_HOME); + private static final char PATH_LIST_SEP = ':'; + + @Inject + public Environment() { + LOG.debug("cryptomator.settingsPath: {}", System.getProperty("cryptomator.settingsPath")); + LOG.debug("cryptomator.ipcPortPath: {}", System.getProperty("cryptomator.ipcPortPath")); + LOG.debug("cryptomator.keychainPath: {}", System.getProperty("cryptomator.ipcPortPath")); + + } + + public Stream getSettingsPath() { + return getPaths("cryptomator.settingsPath"); + } + + public Stream getIpcPortPath() { + return getPaths("cryptomator.ipcPortPath"); + } + + public Stream getKeychainPath() { + return getPaths("cryptomator.keychainPath"); + } + + // visible for testing + Stream getPaths(String propertyName) { + Stream rawSettingsPaths = getRawList(propertyName, PATH_LIST_SEP); + return rawSettingsPaths.filter(Predicate.not(Strings::isNullOrEmpty)).map(Paths::get).map(this::replaceHomeDir); + } + + private Path replaceHomeDir(Path path) { + if (path.startsWith(RELATIVE_HOME_DIR)) { + return ABSOLUTE_HOME_DIR.resolve(RELATIVE_HOME_DIR.relativize(path)); + } else { + return path; + } + } + + private Stream getRawList(String propertyName, char separator) { + String value = System.getProperty(propertyName); + if (value == null) { + return Stream.empty(); + } else { + Iterable iter = Splitter.on(separator).split(value); + Spliterator spliter = Spliterators.spliteratorUnknownSize(iter.iterator(), Spliterator.ORDERED | Spliterator.IMMUTABLE); + return StreamSupport.stream(spliter, false); + } + } + +} diff --git a/main/commons/src/main/java/org/cryptomator/common/settings/SettingsProvider.java b/main/commons/src/main/java/org/cryptomator/common/settings/SettingsProvider.java index 48b85ef96..f10014db0 100644 --- a/main/commons/src/main/java/org/cryptomator/common/settings/SettingsProvider.java +++ b/main/commons/src/main/java/org/cryptomator/common/settings/SettingsProvider.java @@ -27,6 +27,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; import javax.inject.Inject; import javax.inject.Provider; @@ -34,6 +35,7 @@ import javax.inject.Singleton; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.common.Environment; import org.cryptomator.common.LazyInitializer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,92 +47,75 @@ import com.google.gson.GsonBuilder; public class SettingsProvider implements Provider { private static final Logger LOG = LoggerFactory.getLogger(SettingsProvider.class); - private static final Path DEFAULT_SETTINGS_PATH; private static final long SAVE_DELAY_MS = 1000; - static { - final FileSystem fs = FileSystems.getDefault(); - if (SystemUtils.IS_OS_WINDOWS) { - DEFAULT_SETTINGS_PATH = fs.getPath(SystemUtils.USER_HOME, "AppData/Roaming/Cryptomator/settings.json"); - } else if (SystemUtils.IS_OS_MAC_OSX) { - DEFAULT_SETTINGS_PATH = fs.getPath(SystemUtils.USER_HOME, "Library/Application Support/Cryptomator/settings.json"); - } else { - DEFAULT_SETTINGS_PATH = fs.getPath(SystemUtils.USER_HOME, ".Cryptomator/settings.json"); - } - } - private final ScheduledExecutorService saveScheduler = Executors.newSingleThreadScheduledExecutor(); private final AtomicReference> scheduledSaveCmd = new AtomicReference<>(); private final AtomicReference settings = new AtomicReference<>(); private final SettingsJsonAdapter settingsJsonAdapter = new SettingsJsonAdapter(); + private final Environment env; private final Gson gson; @Inject - public SettingsProvider() { + public SettingsProvider(Environment env) { + this.env = env; this.gson = new GsonBuilder() // .setPrettyPrinting().setLenient().disableHtmlEscaping() // .registerTypeAdapter(Settings.class, settingsJsonAdapter) // .create(); } - private Path getSettingsPath() { - final String settingsPathProperty = System.getProperty("cryptomator.settingsPath"); - return Optional.ofNullable(settingsPathProperty).filter(StringUtils::isNotBlank).map(this::replaceHomeDir).map(FileSystems.getDefault()::getPath).orElse(DEFAULT_SETTINGS_PATH); - } - - private String replaceHomeDir(String path) { - if (path.startsWith("~/")) { - return SystemUtils.USER_HOME + path.substring(1); - } else { - return path; - } - } - @Override public Settings get() { return LazyInitializer.initializeLazily(settings, this::load); } private Settings load() { - Settings settings; - final Path settingsPath = getSettingsPath(); - try (InputStream in = Files.newInputStream(settingsPath, StandardOpenOption.READ); // - Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) { - settings = gson.fromJson(reader, Settings.class); + Settings settings = env.getSettingsPath().flatMap(this::tryLoad).findFirst().orElse(new Settings()); + settings.setSaveCmd(this::scheduleSave); + return settings; + } + + private Stream tryLoad(Path path) { + LOG.debug("Attempting to load settings from {}", path); + try (InputStream in = Files.newInputStream(path, StandardOpenOption.READ); // + Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) { + Settings settings = gson.fromJson(reader, Settings.class); if (settings == null) { throw new IOException("Unexpected EOF"); } - LOG.info("Settings loaded from " + settingsPath); + LOG.info("Settings loaded from {}", path); + return Stream.of(settings); } catch (IOException e) { LOG.info("Failed to load settings, creating new one."); - settings = new Settings(); + return Stream.empty(); } - settings.setSaveCmd(this::scheduleSave); - return settings; } private void scheduleSave(Settings settings) { if (settings == null) { return; } - ScheduledFuture saveCmd = saveScheduler.schedule(() -> { - this.save(settings); - }, SAVE_DELAY_MS, TimeUnit.MILLISECONDS); - ScheduledFuture previousSaveCmd = scheduledSaveCmd.getAndSet(saveCmd); - if (previousSaveCmd != null) { - previousSaveCmd.cancel(false); - } + final Optional settingsPath = env.getSettingsPath().findFirst(); // alway save to preferred (first) path + settingsPath.ifPresent(path -> { + Runnable saveCommand = () -> this.save(settings, path); + ScheduledFuture scheduledTask = saveScheduler.schedule(saveCommand, SAVE_DELAY_MS, TimeUnit.MILLISECONDS); + ScheduledFuture previouslyScheduledTask = scheduledSaveCmd.getAndSet(scheduledTask); + if (previouslyScheduledTask != null) { + previouslyScheduledTask.cancel(false); + } + }); } - private void save(Settings settings) { + private void save(Settings settings, Path settingsPath) { assert settings != null : "method should only be invoked by #scheduleSave, which checks for null"; - final Path settingsPath = getSettingsPath(); + LOG.debug("Attempting to save settings to {}", settingsPath); try { Files.createDirectories(settingsPath.getParent()); try (OutputStream out = Files.newOutputStream(settingsPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); // Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8)) { gson.toJson(settings, writer); - LOG.info("Settings saved to " + settingsPath); + LOG.info("Settings saved to {}", settingsPath); } } catch (IOException e) { LOG.error("Failed to save settings.", e); diff --git a/main/commons/src/test/java/org/cryptomator/common/EnvironmentTest.java b/main/commons/src/test/java/org/cryptomator/common/EnvironmentTest.java new file mode 100644 index 000000000..8fe899630 --- /dev/null +++ b/main/commons/src/test/java/org/cryptomator/common/EnvironmentTest.java @@ -0,0 +1,110 @@ +package org.cryptomator.common; + +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.stream.Collectors; + +@DisplayName("Environment Variables Test") +class EnvironmentTest { + + private Environment env; + + @BeforeAll + static void init() { + System.setProperty("user.home", "/home/testuser"); + } + + @BeforeEach + void initEach() { + env = new Environment(); + } + + @Test + @DisplayName("cryptomator.settingsPath=~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json") + public void testSettingsPath() { + System.setProperty("cryptomator.settingsPath", "~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json"); + + List result = env.getSettingsPath().collect(Collectors.toList()); + MatcherAssert.assertThat(result, Matchers.hasSize(2)); + MatcherAssert.assertThat(result, Matchers.contains(Paths.get("/home/testuser/.config/Cryptomator/settings.json"), + Paths.get("/home/testuser/.Cryptomator/settings.json"))); + } + + @Test + @DisplayName("cryptomator.ipcPortPath=~/.config/Cryptomator/ipcPort.bin:~/.Cryptomator/ipcPort.bin") + public void testIpcPortPath() { + System.setProperty("cryptomator.ipcPortPath", "~/.config/Cryptomator/ipcPort.bin:~/.Cryptomator/ipcPort.bin"); + + List result = env.getIpcPortPath().collect(Collectors.toList()); + MatcherAssert.assertThat(result, Matchers.hasSize(2)); + MatcherAssert.assertThat(result, Matchers.contains(Paths.get("/home/testuser/.config/Cryptomator/ipcPort.bin"), + Paths.get("/home/testuser/.Cryptomator/ipcPort.bin"))); + } + + @Test + @DisplayName("cryptomator.keychainPath=~/AppData/Roaming/Cryptomator/keychain.json") + public void testKeychainPath() { + System.setProperty("cryptomator.keychainPath", "~/AppData/Roaming/Cryptomator/keychain.json"); + + List result = env.getKeychainPath().collect(Collectors.toList()); + MatcherAssert.assertThat(result, Matchers.hasSize(1)); + MatcherAssert.assertThat(result, Matchers.contains(Paths.get("/home/testuser/AppData/Roaming/Cryptomator/keychain.json"))); + } + + @Nested + @DisplayName("Path Lists") + class SettingsPath { + + @Test + @DisplayName("test.path.property=") + public void testEmptyList() { + System.setProperty("test.path.property", ""); + List result = env.getPaths("test.path.property").collect(Collectors.toList()); + + MatcherAssert.assertThat(result, Matchers.hasSize(0)); + } + + @Test + @DisplayName("test.path.property=/foo/bar/test") + public void testSingleAbsolutePath() { + System.setProperty("test.path.property", "/foo/bar/test"); + List result = env.getPaths("test.path.property").collect(Collectors.toList()); + + MatcherAssert.assertThat(result, Matchers.hasSize(1)); + MatcherAssert.assertThat(result, Matchers.hasItem(Paths.get("/foo/bar/test"))); + } + + @Test + @DisplayName("test.path.property=~/test") + public void testSingleHomeRelativePath() { + System.setProperty("test.path.property", "~/test"); + List result = env.getPaths("test.path.property").collect(Collectors.toList()); + + MatcherAssert.assertThat(result, Matchers.hasSize(1)); + MatcherAssert.assertThat(result, Matchers.hasItem(Paths.get("/home/testuser/test"))); + } + + @Test + @DisplayName("test.path.property=~/test:~/test2:/foo/bar/test") + public void testMultiplePaths() { + System.setProperty("test.path.property", "~/test:~/test2:/foo/bar/test"); + List result = env.getPaths("test.path.property").collect(Collectors.toList()); + + MatcherAssert.assertThat(result, Matchers.hasSize(3)); + MatcherAssert.assertThat(result, Matchers.contains(Paths.get("/home/testuser/test"), + Paths.get("/home/testuser/test2"), + Paths.get("/foo/bar/test"))); + } + + } + +} From 79306ea4986b1f1cfbb952b1334e65a0cebf7820 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Tue, 19 Feb 2019 00:11:58 +0100 Subject: [PATCH 12/44] Introduced new scope @FxApplicationScoped below @Singleton. This allows us to initialize Dagger before we start the JavaFX application, which will in turn allow us to access Environment from within the IPC classes. Affects #663 --- .../org/cryptomator/common/Environment.java | 2 - .../common/FxApplicationScoped.java | 13 ++++ .../keychain/TestKeychainComponent.java | 5 +- .../launcher/ApplicationVersion.java | 20 ------ .../org/cryptomator/launcher/Cryptomator.java | 67 ++++++++++++++---- .../launcher/CryptomatorComponent.java | 26 +++++++ .../launcher/CryptomatorModule.java | 30 ++++++++ .../launcher/FileOpenRequestHandler.java | 17 +++-- ...onent.java => FxApplicationComponent.java} | 28 ++++++-- .../launcher/FxApplicationModule.java | 26 +++++++ .../launcher/InterProcessCommunicator.java | 26 +------ .../cryptomator/launcher/LauncherModule.java | 68 ------------------- .../cryptomator/launcher/MainApplication.java | 45 ------------ .../org/cryptomator/logging/DebugMode.java | 20 +++--- .../java/org/cryptomator/ui/ExitUtil.java | 40 ++++++----- .../java/org/cryptomator/ui/UiModule.java | 32 ++++----- .../ui/controllers/MainController.java | 37 +++++----- .../ui/controllers/NotFoundController.java | 8 +-- .../ui/controllers/SettingsController.java | 4 +- .../ui/controllers/ViewControllerLoader.java | 16 ++--- .../ui/controllers/WelcomeController.java | 34 +++++----- .../org/cryptomator/ui/l10n/Localization.java | 18 +++-- .../cryptomator/ui/model/AutoUnlocker.java | 17 +++-- .../ui/model/UpgradeStrategies.java | 8 +-- .../UpgradeVersion3DropBundleExtension.java | 16 ++--- .../ui/model/UpgradeVersion3to4.java | 22 +++--- .../ui/model/UpgradeVersion4to5.java | 21 +++--- .../ui/model/UpgradeVersion5toX.java | 11 ++- .../cryptomator/ui/model/VaultComponent.java | 6 +- .../cryptomator/ui/model/VaultFactory.java | 14 ++-- .../org/cryptomator/ui/model/VaultList.java | 19 +++--- .../org/cryptomator/ui/model/VaultModule.java | 31 +++------ .../ui/model/WindowsDriveLetters.java | 13 ++-- .../ui/util/PasswordStrengthUtil.java | 17 ++--- 34 files changed, 372 insertions(+), 405 deletions(-) create mode 100644 main/commons/src/main/java/org/cryptomator/common/FxApplicationScoped.java delete mode 100644 main/launcher/src/main/java/org/cryptomator/launcher/ApplicationVersion.java create mode 100644 main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java create mode 100644 main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java rename main/launcher/src/main/java/org/cryptomator/launcher/{LauncherComponent.java => FxApplicationComponent.java} (51%) create mode 100644 main/launcher/src/main/java/org/cryptomator/launcher/FxApplicationModule.java delete mode 100644 main/launcher/src/main/java/org/cryptomator/launcher/LauncherModule.java delete mode 100644 main/launcher/src/main/java/org/cryptomator/launcher/MainApplication.java diff --git a/main/commons/src/main/java/org/cryptomator/common/Environment.java b/main/commons/src/main/java/org/cryptomator/common/Environment.java index e094f4257..6b582d7f0 100644 --- a/main/commons/src/main/java/org/cryptomator/common/Environment.java +++ b/main/commons/src/main/java/org/cryptomator/common/Environment.java @@ -1,6 +1,5 @@ package org.cryptomator.common; -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Splitter; import com.google.common.base.Strings; import org.slf4j.Logger; @@ -30,7 +29,6 @@ public class Environment { LOG.debug("cryptomator.settingsPath: {}", System.getProperty("cryptomator.settingsPath")); LOG.debug("cryptomator.ipcPortPath: {}", System.getProperty("cryptomator.ipcPortPath")); LOG.debug("cryptomator.keychainPath: {}", System.getProperty("cryptomator.ipcPortPath")); - } public Stream getSettingsPath() { diff --git a/main/commons/src/main/java/org/cryptomator/common/FxApplicationScoped.java b/main/commons/src/main/java/org/cryptomator/common/FxApplicationScoped.java new file mode 100644 index 000000000..dd90f4a38 --- /dev/null +++ b/main/commons/src/main/java/org/cryptomator/common/FxApplicationScoped.java @@ -0,0 +1,13 @@ +package org.cryptomator.common; + +import javax.inject.Scope; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Scope +@Documented +@Retention(RetentionPolicy.RUNTIME) +public @interface FxApplicationScoped { + +} diff --git a/main/keychain/src/test/java/org/cryptomator/keychain/TestKeychainComponent.java b/main/keychain/src/test/java/org/cryptomator/keychain/TestKeychainComponent.java index 5d954bad0..82c4b82ae 100644 --- a/main/keychain/src/test/java/org/cryptomator/keychain/TestKeychainComponent.java +++ b/main/keychain/src/test/java/org/cryptomator/keychain/TestKeychainComponent.java @@ -5,11 +5,10 @@ *******************************************************************************/ package org.cryptomator.keychain; -import java.util.Optional; +import dagger.Component; import javax.inject.Singleton; - -import dagger.Component; +import java.util.Optional; @Singleton @Component(modules = KeychainModule.class) diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/ApplicationVersion.java b/main/launcher/src/main/java/org/cryptomator/launcher/ApplicationVersion.java deleted file mode 100644 index 6a60c5e86..000000000 --- a/main/launcher/src/main/java/org/cryptomator/launcher/ApplicationVersion.java +++ /dev/null @@ -1,20 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.launcher; - -import java.util.Optional; - -public class ApplicationVersion { - - public static String orElse(String other) { - return get().orElse(other); - } - - public static Optional get() { - return Optional.ofNullable(Cryptomator.class.getPackage().getImplementationVersion()); - } - -} diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java b/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java index 8a97e336d..11a49112d 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java @@ -5,42 +5,83 @@ *******************************************************************************/ package org.cryptomator.launcher; -import java.io.IOException; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; - +import javafx.application.Application; +import javafx.stage.Stage; import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.ui.controllers.MainController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javafx.application.Application; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Optional; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; public class Cryptomator { private static final Logger LOG = LoggerFactory.getLogger(Cryptomator.class); - static final BlockingQueue FILE_OPEN_REQUESTS = new ArrayBlockingQueue<>(10); + private static final CryptomatorComponent CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.create(); + private static final Path DEFAULT_IPC_PATH = Paths.get(".ipcPort.tmp"); + + // We need a separate FX Application class. + // If org.cryptomator.launcher.Cryptomator simply extended Application, the module system magically kicks in and throws exceptions + public static class MainApp extends Application { + + private Stage primaryStage; + + @Override + public void start(Stage primaryStage) { + LOG.info("JavaFX application started."); + this.primaryStage = primaryStage; + primaryStage.setMinWidth(652.0); + primaryStage.setMinHeight(440.0); + + FxApplicationComponent fxApplicationComponent = CRYPTOMATOR_COMPONENT.fxApplicationComponent() // + .fxApplication(this) // + .mainWindow(primaryStage) // + .build(); + + fxApplicationComponent.debugMode().initialize(); + + MainController mainCtrl = fxApplicationComponent.fxmlLoader().load("/fxml/main.fxml"); + mainCtrl.initStage(primaryStage); + primaryStage.show(); + } + + @Override + public void stop() { + assert primaryStage != null; + primaryStage.hide(); + LOG.info("JavaFX application stopped."); + } + + } public static void main(String[] args) { - LOG.info("Starting Cryptomator {} on {} {} ({})", ApplicationVersion.orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH); + LOG.info("Starting Cryptomator {} on {} {} ({})", CRYPTOMATOR_COMPONENT.applicationVersion().orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH); - FileOpenRequestHandler fileOpenRequestHandler = new FileOpenRequestHandler(FILE_OPEN_REQUESTS); - try (InterProcessCommunicator communicator = InterProcessCommunicator.start(new IpcProtocolImpl(fileOpenRequestHandler))) { + FileOpenRequestHandler fileOpenRequestHandler = CRYPTOMATOR_COMPONENT.fileOpenRequestHanlder(); + Path ipcPortPath = CRYPTOMATOR_COMPONENT.environment().getIpcPortPath().findFirst().orElse(DEFAULT_IPC_PATH); + try (InterProcessCommunicator communicator = InterProcessCommunicator.start(ipcPortPath, new IpcProtocolImpl(fileOpenRequestHandler))) { if (communicator.isServer()) { fileOpenRequestHandler.handleLaunchArgs(args); CleanShutdownPerformer.registerShutdownHook(); - Application.launch(MainApplication.class, args); + Application.launch(MainApp.class, args); } else { communicator.handleLaunchArgs(args); LOG.info("Found running application instance. Shutting down."); } + System.exit(0); // end remaining non-daemon threads. } catch (IOException e) { LOG.error("Failed to initiate inter-process communication.", e); + System.exit(2); } catch (Throwable e) { LOG.error("Error during startup", e); + System.exit(1); } - System.exit(0); // end remaining non-daemon threads. } private static class IpcProtocolImpl implements InterProcessCommunicationProtocol { diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java new file mode 100644 index 000000000..6cd57024e --- /dev/null +++ b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java @@ -0,0 +1,26 @@ +package org.cryptomator.launcher; + +import dagger.Component; +import org.cryptomator.common.CommonsModule; +import org.cryptomator.common.Environment; + +import javax.inject.Named; +import javax.inject.Singleton; +import java.nio.file.Path; +import java.util.Optional; +import java.util.concurrent.BlockingQueue; + +@Singleton +@Component(modules = {CryptomatorModule.class, CommonsModule.class}) +public interface CryptomatorComponent { + + Environment environment(); + + FileOpenRequestHandler fileOpenRequestHanlder(); + + @Named("applicationVersion") + Optional applicationVersion(); + + FxApplicationComponent.Builder fxApplicationComponent(); + +} diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java new file mode 100644 index 000000000..0efe18499 --- /dev/null +++ b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java @@ -0,0 +1,30 @@ +package org.cryptomator.launcher; + +import dagger.Module; +import dagger.Provides; + +import javax.inject.Named; +import javax.inject.Singleton; +import java.nio.file.Path; +import java.util.Optional; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; + +@Module +class CryptomatorModule { + + @Provides + @Singleton + @Named("fileOpenRequests") + BlockingQueue provideFileOpenRequests() { + return new ArrayBlockingQueue<>(10); + } + + @Provides + @Singleton + @Named("applicationVersion") + Optional provideApplicationVersion() { + return Optional.ofNullable(Cryptomator.class.getPackage().getImplementationVersion()); + } + +} diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java b/main/launcher/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java index 8f10c6aa2..663f01b3c 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java @@ -7,6 +7,7 @@ package org.cryptomator.launcher; import java.awt.Desktop; +import java.awt.desktop.OpenFilesEvent; import java.io.File; import java.nio.file.FileSystem; import java.nio.file.FileSystems; @@ -17,22 +18,30 @@ import java.util.concurrent.BlockingQueue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +@Singleton class FileOpenRequestHandler { private static final Logger LOG = LoggerFactory.getLogger(FileOpenRequestHandler.class); private final BlockingQueue fileOpenRequests; - public FileOpenRequestHandler(BlockingQueue fileOpenRequests) { + @Inject + public FileOpenRequestHandler(@Named("fileOpenRequests") BlockingQueue fileOpenRequests) { this.fileOpenRequests = fileOpenRequests; try { - Desktop.getDesktop().setOpenFileHandler(e -> { - e.getFiles().stream().map(File::toPath).forEach(fileOpenRequests::add); - }); + Desktop.getDesktop().setOpenFileHandler(this::openFiles); } catch (UnsupportedOperationException e) { LOG.info("Unable to setOpenFileHandler, probably not supported on this OS."); } } + private void openFiles(final OpenFilesEvent evt) { + evt.getFiles().stream().map(File::toPath).forEach(fileOpenRequests::add); + } + public void handleLaunchArgs(String[] args) { handleLaunchArgs(FileSystems.getDefault(), args); } diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/LauncherComponent.java b/main/launcher/src/main/java/org/cryptomator/launcher/FxApplicationComponent.java similarity index 51% rename from main/launcher/src/main/java/org/cryptomator/launcher/LauncherComponent.java rename to main/launcher/src/main/java/org/cryptomator/launcher/FxApplicationComponent.java index bf70359fb..d6985bf88 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/LauncherComponent.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/FxApplicationComponent.java @@ -5,19 +5,35 @@ *******************************************************************************/ package org.cryptomator.launcher; -import javax.inject.Singleton; - +import dagger.BindsInstance; +import dagger.Subcomponent; +import javafx.application.Application; +import javafx.stage.Stage; +import org.cryptomator.common.FxApplicationScoped; import org.cryptomator.logging.DebugMode; import org.cryptomator.ui.controllers.ViewControllerLoader; -import dagger.Component; +import javax.inject.Named; -@Singleton -@Component(modules = LauncherModule.class) -interface LauncherComponent { +@FxApplicationScoped +@Subcomponent(modules = FxApplicationModule.class) +interface FxApplicationComponent { ViewControllerLoader fxmlLoader(); DebugMode debugMode(); + @Subcomponent.Builder + interface Builder { + + @BindsInstance + Builder fxApplication(Application application); + + @BindsInstance + Builder mainWindow(@Named("mainWindow") Stage mainWindow); + + FxApplicationComponent build(); + + } + } diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/FxApplicationModule.java b/main/launcher/src/main/java/org/cryptomator/launcher/FxApplicationModule.java new file mode 100644 index 000000000..1d9988d06 --- /dev/null +++ b/main/launcher/src/main/java/org/cryptomator/launcher/FxApplicationModule.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ +package org.cryptomator.launcher; + +import dagger.Module; +import dagger.Provides; +import org.cryptomator.common.FxApplicationScoped; +import org.cryptomator.ui.UiModule; + +import javax.inject.Named; +import java.util.function.Consumer; + +@Module(includes = {UiModule.class}) +class FxApplicationModule { + + @Provides + @FxApplicationScoped + @Named("shutdownTaskScheduler") + Consumer provideShutdownTaskScheduler() { + return CleanShutdownPerformer::scheduleShutdownTask; + } + +} diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/InterProcessCommunicator.java b/main/launcher/src/main/java/org/cryptomator/launcher/InterProcessCommunicator.java index e9108cb75..9363b6246 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/InterProcessCommunicator.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/InterProcessCommunicator.java @@ -48,16 +48,12 @@ abstract class InterProcessCommunicator implements InterProcessCommunicationProt public abstract boolean isServer(); /** + * @param portFilePath Path to a file containing the IPC port * @param endpoint The server-side communication endpoint. * @return Either a client or a server communicator. * @throws IOException In case of communication errors. */ - public static InterProcessCommunicator start(InterProcessCommunicationProtocol endpoint) throws IOException { - return start(getIpcPortPath(), endpoint); - } - - // visible for testing - static InterProcessCommunicator start(Path portFilePath, InterProcessCommunicationProtocol endpoint) throws IOException { + public static InterProcessCommunicator start(Path portFilePath, InterProcessCommunicationProtocol endpoint) throws IOException { System.setProperty("java.rmi.server.hostname", "localhost"); try { // try to connect to existing server: @@ -76,24 +72,6 @@ abstract class InterProcessCommunicator implements InterProcessCommunicationProt return server; } - private static Path getIpcPortPath() { - final String settingsPathProperty = System.getProperty("cryptomator.ipcPortPath"); - if (settingsPathProperty == null) { - LOG.warn("System property cryptomator.ipcPortPath not set."); - return Paths.get(".ipcPort.tmp"); - } else { - return Paths.get(replaceHomeDir(settingsPathProperty)); - } - } - - private static String replaceHomeDir(String path) { - if (path.startsWith("~/")) { - return SystemUtils.USER_HOME + path.substring(1); - } else { - return path; - } - } - public static class ClientCommunicator extends InterProcessCommunicator { private final IpcProtocolRemote remote; diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/LauncherModule.java b/main/launcher/src/main/java/org/cryptomator/launcher/LauncherModule.java deleted file mode 100644 index 2e1cd6c53..000000000 --- a/main/launcher/src/main/java/org/cryptomator/launcher/LauncherModule.java +++ /dev/null @@ -1,68 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.launcher; - -import java.nio.file.Path; -import java.util.Optional; -import java.util.concurrent.BlockingQueue; -import java.util.function.Consumer; - -import javax.inject.Named; -import javax.inject.Singleton; - -import org.cryptomator.ui.UiModule; - -import dagger.Module; -import dagger.Provides; -import javafx.application.Application; -import javafx.stage.Stage; - -@Module(includes = {UiModule.class}) -class LauncherModule { - - private final Application application; - private final Stage mainWindow; - - public LauncherModule(Application application, Stage mainWindow) { - this.application = application; - this.mainWindow = mainWindow; - } - - @Provides - @Singleton - Application provideApplication() { - return application; - } - - @Provides - @Singleton - @Named("applicationVersion") - Optional provideApplicationVersion() { - return ApplicationVersion.get(); - } - - @Provides - @Singleton - @Named("mainWindow") - Stage provideMainWindow() { - return mainWindow; - } - - @Provides - @Singleton - @Named("fileOpenRequests") - BlockingQueue provideFileOpenRequests() { - return Cryptomator.FILE_OPEN_REQUESTS; - } - - @Provides - @Singleton - @Named("shutdownTaskScheduler") - Consumer provideShutdownTaskScheduler() { - return CleanShutdownPerformer::scheduleShutdownTask; - } - -} diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/MainApplication.java b/main/launcher/src/main/java/org/cryptomator/launcher/MainApplication.java deleted file mode 100644 index c62f624c4..000000000 --- a/main/launcher/src/main/java/org/cryptomator/launcher/MainApplication.java +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.launcher; - -import javafx.application.Application; -import javafx.stage.Stage; -import org.cryptomator.ui.controllers.MainController; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class MainApplication extends Application { - - private static final Logger LOG = LoggerFactory.getLogger(MainApplication.class); - private Stage primaryStage; - - @Override - public void start(Stage primaryStage) { - LOG.info("JavaFX application started."); - this.primaryStage = primaryStage; - primaryStage.setMinWidth(652.0); - primaryStage.setMinHeight(440.0); - - LauncherModule launcherModule = new LauncherModule(this, primaryStage); - LauncherComponent launcherComponent = DaggerLauncherComponent.builder() // - .launcherModule(launcherModule) // - .build(); - - launcherComponent.debugMode().initialize(); - - MainController mainCtrl = launcherComponent.fxmlLoader().load("/fxml/main.fxml"); - mainCtrl.initStage(primaryStage); - primaryStage.show(); - } - - @Override - public void stop() { - assert primaryStage != null; - primaryStage.hide(); - LOG.info("JavaFX application stopped."); - } - -} diff --git a/main/launcher/src/main/java/org/cryptomator/logging/DebugMode.java b/main/launcher/src/main/java/org/cryptomator/logging/DebugMode.java index 911e3a05d..7f18bd5c3 100644 --- a/main/launcher/src/main/java/org/cryptomator/logging/DebugMode.java +++ b/main/launcher/src/main/java/org/cryptomator/logging/DebugMode.java @@ -5,22 +5,20 @@ *******************************************************************************/ package org.cryptomator.logging; -import static java.util.Arrays.asList; - -import java.util.Collection; - -import javax.inject.Inject; -import javax.inject.Singleton; - +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import org.cryptomator.common.FxApplicationScoped; import org.cryptomator.common.settings.Settings; import org.slf4j.ILoggerFactory; import org.slf4j.LoggerFactory; -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; +import javax.inject.Inject; +import java.util.Collection; -@Singleton +import static java.util.Arrays.asList; + +@FxApplicationScoped public class DebugMode { private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(DebugMode.class); diff --git a/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java b/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java index eca371bbf..ba51b6622 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java +++ b/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java @@ -9,6 +9,24 @@ *******************************************************************************/ package org.cryptomator.ui; +import javafx.application.Platform; +import javafx.stage.Stage; +import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.common.FxApplicationScoped; +import org.cryptomator.common.settings.Settings; +import org.cryptomator.jni.JniException; +import org.cryptomator.jni.MacApplicationUiState; +import org.cryptomator.jni.MacFunctions; +import org.cryptomator.ui.l10n.Localization; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import javax.swing.SwingUtilities; import java.awt.AWTException; import java.awt.Image; import java.awt.MenuItem; @@ -24,27 +42,7 @@ import java.io.IOException; import java.util.Optional; import java.util.concurrent.TimeUnit; -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; -import javax.swing.SwingUtilities; - -import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.common.settings.Settings; -import org.cryptomator.jni.JniException; -import org.cryptomator.jni.MacApplicationUiState; -import org.cryptomator.jni.MacFunctions; -import org.cryptomator.ui.l10n.Localization; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javafx.application.Platform; -import javafx.stage.Stage; - -@Singleton +@FxApplicationScoped public class ExitUtil { private static final Logger LOG = LoggerFactory.getLogger(ExitUtil.class); diff --git a/main/ui/src/main/java/org/cryptomator/ui/UiModule.java b/main/ui/src/main/java/org/cryptomator/ui/UiModule.java index 8b94936b0..ee56d5a35 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/UiModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/UiModule.java @@ -8,21 +8,11 @@ *******************************************************************************/ package org.cryptomator.ui; -import java.net.InetSocketAddress; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; - -import javax.inject.Named; -import javax.inject.Singleton; - import dagger.Module; import dagger.Provides; import javafx.beans.binding.Binding; import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.common.CommonsModule; +import org.cryptomator.common.FxApplicationScoped; import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.SettingsProvider; import org.cryptomator.frontend.webdav.WebDavServer; @@ -31,19 +21,27 @@ import org.cryptomator.ui.controllers.ViewControllerModule; import org.cryptomator.ui.model.VaultComponent; import org.fxmisc.easybind.EasyBind; -@Module(includes = {ViewControllerModule.class, CommonsModule.class, KeychainModule.class}, subcomponents = {VaultComponent.class}) +import javax.inject.Named; +import java.net.InetSocketAddress; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; + +@Module(includes = {ViewControllerModule.class, KeychainModule.class}, subcomponents = {VaultComponent.class}) public class UiModule { private static final int NUM_SCHEDULER_THREADS = 4; @Provides - @Singleton + @FxApplicationScoped Settings provideSettings(SettingsProvider settingsProvider) { return settingsProvider.get(); } @Provides - @Singleton + @FxApplicationScoped ScheduledExecutorService provideScheduledExecutorService(@Named("shutdownTaskScheduler") Consumer shutdownTaskScheduler) { final AtomicInteger threadNumber = new AtomicInteger(1); ScheduledExecutorService executorService = Executors.newScheduledThreadPool(NUM_SCHEDULER_THREADS, r -> { @@ -57,7 +55,7 @@ public class UiModule { } @Provides - @Singleton + @FxApplicationScoped ExecutorService provideExecutorService(@Named("shutdownTaskScheduler") Consumer shutdownTaskScheduler) { final AtomicInteger threadNumber = new AtomicInteger(1); ExecutorService executorService = Executors.newCachedThreadPool(r -> { @@ -71,7 +69,7 @@ public class UiModule { } @Provides - @Singleton + @FxApplicationScoped Binding provideServerSocketAddressBinding(Settings settings) { return EasyBind.map(settings.port(), (Number port) -> { String host = SystemUtils.IS_OS_WINDOWS ? "127.0.0.1" : "localhost"; @@ -80,7 +78,7 @@ public class UiModule { } @Provides - @Singleton + @FxApplicationScoped WebDavServer provideWebDavServer(Binding serverSocketAddressBinding) { WebDavServer server = WebDavServer.create(); // no need to unsubscribe eventually, because server is a singleton diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java index b1a823178..3a0092804 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java @@ -9,24 +9,6 @@ ******************************************************************************/ package org.cryptomator.ui.controllers; -import java.awt.Desktop; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ExecutorService; -import java.util.stream.Stream; - -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; - import javafx.application.Application; import javafx.application.Platform; import javafx.beans.binding.Binding; @@ -62,6 +44,7 @@ import javafx.scene.text.Font; import javafx.stage.FileChooser; import javafx.stage.Stage; import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.common.FxApplicationScoped; import org.cryptomator.common.settings.VaultSettings; import org.cryptomator.ui.ExitUtil; import org.cryptomator.ui.controls.DirectoryListCell; @@ -79,9 +62,25 @@ import org.fxmisc.easybind.monadic.MonadicBinding; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.inject.Inject; +import javax.inject.Named; +import java.awt.Desktop; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.stream.Stream; + import static org.cryptomator.ui.util.DialogBuilderUtil.buildErrorDialog; -@Singleton +@FxApplicationScoped public class MainController implements ViewController { private static final Logger LOG = LoggerFactory.getLogger(MainController.class); diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/NotFoundController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/NotFoundController.java index ef2e44a07..8d4408111 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/NotFoundController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/NotFoundController.java @@ -5,14 +5,14 @@ *******************************************************************************/ package org.cryptomator.ui.controllers; -import javax.inject.Inject; -import javax.inject.Singleton; - import javafx.fxml.FXML; import javafx.scene.Parent; import javafx.scene.layout.VBox; +import org.cryptomator.common.FxApplicationScoped; -@Singleton +import javax.inject.Inject; + +@FxApplicationScoped public class NotFoundController implements ViewController { @Inject diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java index 8c1454404..70a792f37 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java @@ -25,6 +25,7 @@ import javafx.scene.input.KeyEvent; import javafx.scene.layout.VBox; import javafx.util.StringConverter; import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.common.FxApplicationScoped; import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.VolumeImpl; import org.cryptomator.ui.l10n.Localization; @@ -32,10 +33,9 @@ import org.cryptomator.ui.model.Volume; import javax.inject.Inject; import javax.inject.Named; -import javax.inject.Singleton; import java.util.Optional; -@Singleton +@FxApplicationScoped public class SettingsController implements ViewController { private static final CharMatcher DIGITS_MATCHER = CharMatcher.inRange('0', '9'); diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/ViewControllerLoader.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/ViewControllerLoader.java index b345dafdc..55f51db83 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/ViewControllerLoader.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/ViewControllerLoader.java @@ -5,20 +5,18 @@ *******************************************************************************/ package org.cryptomator.ui.controllers; +import javafx.fxml.FXMLLoader; +import org.cryptomator.common.FxApplicationScoped; +import org.cryptomator.ui.l10n.Localization; + +import javax.inject.Inject; +import javax.inject.Provider; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; import java.util.Map; -import javax.inject.Inject; -import javax.inject.Provider; -import javax.inject.Singleton; - -import org.cryptomator.ui.l10n.Localization; - -import javafx.fxml.FXMLLoader; - -@Singleton +@FxApplicationScoped public class ViewControllerLoader { private final Map, Provider> controllerProviders; diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java index 353576862..30d09b972 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java @@ -8,22 +8,6 @@ ******************************************************************************/ package org.cryptomator.ui.controllers; -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.URI; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.Comparator; -import java.util.Map; -import java.util.Optional; -import java.util.ResourceBundle; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; @@ -39,15 +23,31 @@ import javafx.scene.control.Label; import javafx.scene.control.ProgressIndicator; import javafx.scene.layout.VBox; import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.common.FxApplicationScoped; import org.cryptomator.common.settings.Settings; import org.cryptomator.ui.l10n.Localization; import org.cryptomator.ui.util.Tasks; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.inject.Inject; +import javax.inject.Named; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Comparator; +import java.util.Map; +import java.util.Optional; +import java.util.ResourceBundle; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + import static org.cryptomator.ui.util.DialogBuilderUtil.buildYesNoDialog; -@Singleton +@FxApplicationScoped public class WelcomeController implements ViewController { private static final Logger LOG = LoggerFactory.getLogger(WelcomeController.class); diff --git a/main/ui/src/main/java/org/cryptomator/ui/l10n/Localization.java b/main/ui/src/main/java/org/cryptomator/ui/l10n/Localization.java index 299392098..565c3d371 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/l10n/Localization.java +++ b/main/ui/src/main/java/org/cryptomator/ui/l10n/Localization.java @@ -5,6 +5,13 @@ *******************************************************************************/ package org.cryptomator.ui.l10n; +import com.google.common.collect.Sets; +import org.apache.commons.lang3.StringUtils; +import org.cryptomator.common.FxApplicationScoped; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -19,16 +26,7 @@ import java.util.Objects; import java.util.PropertyResourceBundle; import java.util.ResourceBundle; -import javax.inject.Inject; -import javax.inject.Singleton; - -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.collect.Sets; - -@Singleton +@FxApplicationScoped public class Localization extends ResourceBundle { private static final Logger LOG = LoggerFactory.getLogger(Localization.class); diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/AutoUnlocker.java b/main/ui/src/main/java/org/cryptomator/ui/model/AutoUnlocker.java index 78b39fc98..b2909cb84 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/AutoUnlocker.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/AutoUnlocker.java @@ -5,6 +5,13 @@ *******************************************************************************/ package org.cryptomator.ui.model; +import org.cryptomator.common.FxApplicationScoped; +import org.cryptomator.cryptolib.api.CryptoException; +import org.cryptomator.keychain.KeychainAccess; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; import java.io.IOException; import java.nio.CharBuffer; import java.util.Arrays; @@ -14,15 +21,7 @@ import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.stream.Collectors; -import javax.inject.Inject; -import javax.inject.Singleton; - -import org.cryptomator.cryptolib.api.CryptoException; -import org.cryptomator.keychain.KeychainAccess; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@Singleton +@FxApplicationScoped public class AutoUnlocker { private static final Logger LOG = LoggerFactory.getLogger(AutoUnlocker.class); diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeStrategies.java b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeStrategies.java index 0d569b430..dbf9f67d3 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeStrategies.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeStrategies.java @@ -5,15 +5,15 @@ *******************************************************************************/ package org.cryptomator.ui.model; +import org.cryptomator.common.FxApplicationScoped; + +import javax.inject.Inject; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Objects; -import javax.inject.Inject; -import javax.inject.Singleton; - -@Singleton +@FxApplicationScoped public class UpgradeStrategies { private final Collection strategies; diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3DropBundleExtension.java b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3DropBundleExtension.java index 1fa8ff306..d3b48a991 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3DropBundleExtension.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3DropBundleExtension.java @@ -5,23 +5,21 @@ *******************************************************************************/ package org.cryptomator.ui.model; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; - -import javax.inject.Inject; -import javax.inject.Singleton; - +import javafx.application.Platform; import org.apache.commons.lang3.StringUtils; +import org.cryptomator.common.FxApplicationScoped; import org.cryptomator.cryptolib.Cryptors; import org.cryptomator.cryptolib.api.Cryptor; import org.cryptomator.ui.l10n.Localization; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javafx.application.Platform; +import javax.inject.Inject; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; -@Singleton +@FxApplicationScoped class UpgradeVersion3DropBundleExtension extends UpgradeStrategy { private static final Logger LOG = LoggerFactory.getLogger(UpgradeVersion3DropBundleExtension.class); diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3to4.java b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3to4.java index cb0981d52..e68293360 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3to4.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3to4.java @@ -5,8 +5,17 @@ *******************************************************************************/ package org.cryptomator.ui.model; +import com.google.common.io.BaseEncoding; +import org.apache.commons.lang3.StringUtils; +import org.cryptomator.common.FxApplicationScoped; +import org.cryptomator.cryptolib.Cryptors; +import org.cryptomator.cryptolib.api.Cryptor; +import org.cryptomator.cryptolib.common.MessageDigestSupplier; +import org.cryptomator.ui.l10n.Localization; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import javax.inject.Inject; -import javax.inject.Singleton; import java.io.IOException; import java.nio.file.FileVisitOption; import java.nio.file.FileVisitResult; @@ -19,22 +28,13 @@ import java.util.EnumSet; import java.util.regex.Matcher; import java.util.regex.Pattern; -import com.google.common.io.BaseEncoding; -import org.apache.commons.lang3.StringUtils; -import org.cryptomator.cryptolib.Cryptors; -import org.cryptomator.cryptolib.api.Cryptor; -import org.cryptomator.cryptolib.common.MessageDigestSupplier; -import org.cryptomator.ui.l10n.Localization; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import static java.nio.charset.StandardCharsets.UTF_8; /** * Contains the collective knowledge of all creatures who were alive during the development of vault format 3. * This class uses no external classes from the crypto or shortening layer by purpose, so we don't need legacy code inside these. */ -@Singleton +@FxApplicationScoped class UpgradeVersion3to4 extends UpgradeStrategy { private static final Logger LOG = LoggerFactory.getLogger(UpgradeVersion3to4.class); diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion4to5.java b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion4to5.java index 89b2847d1..da421f448 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion4to5.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion4to5.java @@ -5,6 +5,15 @@ *******************************************************************************/ package org.cryptomator.ui.model; +import org.cryptomator.common.FxApplicationScoped; +import org.cryptomator.cryptolib.Cryptors; +import org.cryptomator.cryptolib.api.Cryptor; +import org.cryptomator.cryptolib.api.FileHeader; +import org.cryptomator.ui.l10n.Localization; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; @@ -18,21 +27,11 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.EnumSet; import java.util.regex.Pattern; -import javax.inject.Inject; -import javax.inject.Singleton; - -import org.cryptomator.cryptolib.Cryptors; -import org.cryptomator.cryptolib.api.Cryptor; -import org.cryptomator.cryptolib.api.FileHeader; -import org.cryptomator.ui.l10n.Localization; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** * Contains the collective knowledge of all creatures who were alive during the development of vault format 3. * This class uses no external classes from the crypto or shortening layer by purpose, so we don't need legacy code inside these. */ -@Singleton +@FxApplicationScoped class UpgradeVersion4to5 extends UpgradeStrategy { private static final Logger LOG = LoggerFactory.getLogger(UpgradeVersion4to5.class); diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion5toX.java b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion5toX.java index a502459b6..6707bdf5c 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion5toX.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion5toX.java @@ -5,11 +5,7 @@ *******************************************************************************/ package org.cryptomator.ui.model; -import java.io.IOException; - -import javax.inject.Inject; -import javax.inject.Singleton; - +import org.cryptomator.common.FxApplicationScoped; import org.cryptomator.cryptofs.migration.Migrators; import org.cryptomator.cryptofs.migration.api.NoApplicableMigratorException; import org.cryptomator.cryptolib.Cryptors; @@ -19,7 +15,10 @@ import org.cryptomator.ui.l10n.Localization; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -@Singleton +import javax.inject.Inject; +import java.io.IOException; + +@FxApplicationScoped class UpgradeVersion5toX extends UpgradeStrategy { private static final Logger LOG = LoggerFactory.getLogger(UpgradeVersion5toX.class); diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/VaultComponent.java b/main/ui/src/main/java/org/cryptomator/ui/model/VaultComponent.java index 527aa4f85..6fafed661 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/VaultComponent.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/VaultComponent.java @@ -5,6 +5,8 @@ *******************************************************************************/ package org.cryptomator.ui.model; +import dagger.BindsInstance; +import org.cryptomator.common.settings.VaultSettings; import org.cryptomator.ui.model.VaultModule.PerVault; import dagger.Subcomponent; @@ -17,7 +19,9 @@ public interface VaultComponent { @Subcomponent.Builder interface Builder { - Builder vaultModule(VaultModule module); + + @BindsInstance + Builder vaultSettings(VaultSettings vaultSettings); VaultComponent build(); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/VaultFactory.java b/main/ui/src/main/java/org/cryptomator/ui/model/VaultFactory.java index 0cf0b8251..6ee402e88 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/VaultFactory.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/VaultFactory.java @@ -8,15 +8,14 @@ *******************************************************************************/ package org.cryptomator.ui.model; +import org.cryptomator.common.FxApplicationScoped; +import org.cryptomator.common.settings.VaultSettings; + +import javax.inject.Inject; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import javax.inject.Inject; -import javax.inject.Singleton; - -import org.cryptomator.common.settings.VaultSettings; - -@Singleton +@FxApplicationScoped public class VaultFactory { private final VaultComponent.Builder vaultComponentBuilder; @@ -32,8 +31,7 @@ public class VaultFactory { } private Vault create(VaultSettings vaultSettings) { - VaultModule module = new VaultModule(vaultSettings); - VaultComponent comp = vaultComponentBuilder.vaultModule(module).build(); + VaultComponent comp = vaultComponentBuilder.vaultSettings(vaultSettings).build(); return comp.vault(); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/VaultList.java b/main/ui/src/main/java/org/cryptomator/ui/model/VaultList.java index 5ce2685fa..fb9c53afa 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/VaultList.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/VaultList.java @@ -5,22 +5,19 @@ *******************************************************************************/ package org.cryptomator.ui.model; -import java.util.List; -import java.util.stream.IntStream; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import org.cryptomator.common.settings.Settings; -import org.cryptomator.common.settings.VaultSettings; - import com.google.common.collect.Lists; - import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.collections.transformation.TransformationList; +import org.cryptomator.common.FxApplicationScoped; +import org.cryptomator.common.settings.Settings; +import org.cryptomator.common.settings.VaultSettings; -@Singleton +import javax.inject.Inject; +import java.util.List; +import java.util.stream.IntStream; + +@FxApplicationScoped public class VaultList extends TransformationList { private final VaultFactory vaultFactory; diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/VaultModule.java b/main/ui/src/main/java/org/cryptomator/ui/model/VaultModule.java index 141ba8975..100137f6f 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/VaultModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/VaultModule.java @@ -5,37 +5,22 @@ *******************************************************************************/ package org.cryptomator.ui.model; +import dagger.Module; +import dagger.Provides; +import org.cryptomator.common.settings.Settings; +import org.cryptomator.common.settings.VolumeImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Scope; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Objects; - -import javax.inject.Scope; - -import org.cryptomator.common.settings.VolumeImpl; -import org.cryptomator.common.settings.Settings; -import org.cryptomator.common.settings.VaultSettings; - -import dagger.Module; -import dagger.Provides; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; @Module public class VaultModule { private static final Logger LOG = LoggerFactory.getLogger(VaultModule.class); - private final VaultSettings vaultSettings; - - public VaultModule(VaultSettings vaultSettings) { - this.vaultSettings = Objects.requireNonNull(vaultSettings); - } - - @Provides - @PerVault - public VaultSettings provideVaultSettings() { - return vaultSettings; - } @Scope @Documented diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/WindowsDriveLetters.java b/main/ui/src/main/java/org/cryptomator/ui/model/WindowsDriveLetters.java index 4c2c3111e..05efef31d 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/WindowsDriveLetters.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/WindowsDriveLetters.java @@ -5,6 +5,11 @@ *******************************************************************************/ package org.cryptomator.ui.model; +import org.apache.commons.lang3.CharUtils; +import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.common.FxApplicationScoped; + +import javax.inject.Inject; import java.nio.file.FileSystems; import java.nio.file.Path; import java.util.Set; @@ -13,13 +18,7 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.StreamSupport; -import javax.inject.Inject; -import javax.inject.Singleton; - -import org.apache.commons.lang3.CharUtils; -import org.apache.commons.lang3.SystemUtils; - -@Singleton +@FxApplicationScoped public final class WindowsDriveLetters { private static final Set D_TO_Z; diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/PasswordStrengthUtil.java b/main/ui/src/main/java/org/cryptomator/ui/util/PasswordStrengthUtil.java index 3f9ccd063..373f698d2 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/util/PasswordStrengthUtil.java +++ b/main/ui/src/main/java/org/cryptomator/ui/util/PasswordStrengthUtil.java @@ -8,24 +8,21 @@ *******************************************************************************/ package org.cryptomator.ui.util; -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import org.cryptomator.ui.l10n.Localization; - import com.google.common.base.Strings; import com.nulabinc.zxcvbn.Zxcvbn; - import javafx.geometry.Insets; import javafx.scene.layout.Background; import javafx.scene.layout.BackgroundFill; import javafx.scene.layout.CornerRadii; import javafx.scene.paint.Color; +import org.cryptomator.common.FxApplicationScoped; +import org.cryptomator.ui.l10n.Localization; -@Singleton +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +@FxApplicationScoped public class PasswordStrengthUtil { private static final int PW_TRUNC_LEN = 100; // truncate very long passwords, since zxcvbn memory and runtime depends vastly on the length From f1c332f455c8c7b7fbf1c1c26cd12932a6070e98 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Tue, 19 Feb 2019 16:46:21 +0100 Subject: [PATCH 13/44] Refactored IPC, fixes #663 --- .idea/compiler.xml | 3 + .idea/modules.xml | 9 + .../org/cryptomator/common/Environment.java | 2 +- .../org/cryptomator/launcher/Cryptomator.java | 74 ++--- .../launcher/CryptomatorComponent.java | 6 +- .../launcher/InterProcessCommunicator.java | 252 ----------------- .../org/cryptomator/launcher/IpcFactory.java | 258 ++++++++++++++++++ ...nicationProtocol.java => IpcProtocol.java} | 9 +- .../cryptomator/launcher/IpcProtocolImpl.java | 28 ++ .../InterProcessCommunicatorTest.java | 102 ------- .../cryptomator/launcher/IpcFactoryTest.java | 71 +++++ 11 files changed, 401 insertions(+), 413 deletions(-) create mode 100644 .idea/modules.xml delete mode 100644 main/launcher/src/main/java/org/cryptomator/launcher/InterProcessCommunicator.java create mode 100644 main/launcher/src/main/java/org/cryptomator/launcher/IpcFactory.java rename main/launcher/src/main/java/org/cryptomator/launcher/{InterProcessCommunicationProtocol.java => IpcProtocol.java} (71%) create mode 100644 main/launcher/src/main/java/org/cryptomator/launcher/IpcProtocolImpl.java delete mode 100644 main/launcher/src/test/java/org/cryptomator/launcher/InterProcessCommunicatorTest.java create mode 100644 main/launcher/src/test/java/org/cryptomator/launcher/IpcFactoryTest.java diff --git a/.idea/compiler.xml b/.idea/compiler.xml index eb6e630bd..90cd3c60e 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -29,5 +29,8 @@ + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 000000000..bca3878d6 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/main/commons/src/main/java/org/cryptomator/common/Environment.java b/main/commons/src/main/java/org/cryptomator/common/Environment.java index 6b582d7f0..b45b743f2 100644 --- a/main/commons/src/main/java/org/cryptomator/common/Environment.java +++ b/main/commons/src/main/java/org/cryptomator/common/Environment.java @@ -28,7 +28,7 @@ public class Environment { public Environment() { LOG.debug("cryptomator.settingsPath: {}", System.getProperty("cryptomator.settingsPath")); LOG.debug("cryptomator.ipcPortPath: {}", System.getProperty("cryptomator.ipcPortPath")); - LOG.debug("cryptomator.keychainPath: {}", System.getProperty("cryptomator.ipcPortPath")); + LOG.debug("cryptomator.keychainPath: {}", System.getProperty("cryptomator.keychainPath")); } public Stream getSettingsPath() { diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java b/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java index 11a49112d..12d1e5a65 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java @@ -13,21 +13,34 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.Optional; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; public class Cryptomator { private static final Logger LOG = LoggerFactory.getLogger(Cryptomator.class); - private static final CryptomatorComponent CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.create(); - private static final Path DEFAULT_IPC_PATH = Paths.get(".ipcPort.tmp"); + private static final CryptomatorComponent CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.create(); // DaggerCryptomatorComponent gets generated by Dagger. Run Maven and include target/generated-sources/annotations in your IDE. - // We need a separate FX Application class. - // If org.cryptomator.launcher.Cryptomator simply extended Application, the module system magically kicks in and throws exceptions + public static void main(String[] args) { + LOG.info("Starting Cryptomator {} on {} {} ({})", CRYPTOMATOR_COMPONENT.applicationVersion().orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH); + + try (IpcFactory.IpcEndpoint endpoint = CRYPTOMATOR_COMPONENT.ipcFactory().create()) { + endpoint.getRemote().handleLaunchArgs(args); // if we are the server, getRemote() returns self. + if (endpoint.isConnectedToRemote()) { + LOG.info("Found running application instance. Shutting down."); + } else { + CleanShutdownPerformer.registerShutdownHook(); + Application.launch(MainApp.class, args); + } + } catch (IOException e) { + LOG.error("Failed to initiate inter-process communication.", e); + System.exit(2); + } catch (Throwable e) { + LOG.error("Error during startup", e); + System.exit(1); + } + System.exit(0); // end remaining non-daemon threads. + } + + // We need a separate FX Application class, until we can use the module system. See https://stackoverflow.com/q/54756176/4014509 public static class MainApp extends Application { private Stage primaryStage; @@ -60,45 +73,4 @@ public class Cryptomator { } - public static void main(String[] args) { - LOG.info("Starting Cryptomator {} on {} {} ({})", CRYPTOMATOR_COMPONENT.applicationVersion().orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH); - - FileOpenRequestHandler fileOpenRequestHandler = CRYPTOMATOR_COMPONENT.fileOpenRequestHanlder(); - Path ipcPortPath = CRYPTOMATOR_COMPONENT.environment().getIpcPortPath().findFirst().orElse(DEFAULT_IPC_PATH); - try (InterProcessCommunicator communicator = InterProcessCommunicator.start(ipcPortPath, new IpcProtocolImpl(fileOpenRequestHandler))) { - if (communicator.isServer()) { - fileOpenRequestHandler.handleLaunchArgs(args); - CleanShutdownPerformer.registerShutdownHook(); - Application.launch(MainApp.class, args); - } else { - communicator.handleLaunchArgs(args); - LOG.info("Found running application instance. Shutting down."); - } - System.exit(0); // end remaining non-daemon threads. - } catch (IOException e) { - LOG.error("Failed to initiate inter-process communication.", e); - System.exit(2); - } catch (Throwable e) { - LOG.error("Error during startup", e); - System.exit(1); - } - } - - private static class IpcProtocolImpl implements InterProcessCommunicationProtocol { - - private final FileOpenRequestHandler fileOpenRequestHandler; - - // TODO: inject? - public IpcProtocolImpl(FileOpenRequestHandler fileOpenRequestHandler) { - this.fileOpenRequestHandler = fileOpenRequestHandler; - } - - @Override - public void handleLaunchArgs(String[] args) { - LOG.info("Received launch args: {}", Arrays.stream(args).reduce((a, b) -> a + ", " + b).orElse("")); - fileOpenRequestHandler.handleLaunchArgs(args); - } - - } - } diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java index 6cd57024e..eaedf7585 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java @@ -6,17 +6,13 @@ import org.cryptomator.common.Environment; import javax.inject.Named; import javax.inject.Singleton; -import java.nio.file.Path; import java.util.Optional; -import java.util.concurrent.BlockingQueue; @Singleton @Component(modules = {CryptomatorModule.class, CommonsModule.class}) public interface CryptomatorComponent { - Environment environment(); - - FileOpenRequestHandler fileOpenRequestHanlder(); + IpcFactory ipcFactory(); @Named("applicationVersion") Optional applicationVersion(); diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/InterProcessCommunicator.java b/main/launcher/src/main/java/org/cryptomator/launcher/InterProcessCommunicator.java deleted file mode 100644 index 9363b6246..000000000 --- a/main/launcher/src/main/java/org/cryptomator/launcher/InterProcessCommunicator.java +++ /dev/null @@ -1,252 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.launcher; - -import java.io.Closeable; -import java.io.IOException; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketException; -import java.net.UnknownHostException; -import java.nio.ByteBuffer; -import java.nio.channels.ReadableByteChannel; -import java.nio.channels.WritableByteChannel; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; -import java.rmi.ConnectException; -import java.rmi.ConnectIOException; -import java.rmi.NotBoundException; -import java.rmi.Remote; -import java.rmi.RemoteException; -import java.rmi.registry.LocateRegistry; -import java.rmi.registry.Registry; -import java.rmi.server.RMIClientSocketFactory; -import java.rmi.server.RMIServerSocketFactory; -import java.rmi.server.RMISocketFactory; -import java.rmi.server.UnicastRemoteObject; - -import org.apache.commons.lang3.SystemUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.io.MoreFiles; - -/** - * First running application on a machine opens a server socket. Further processes will connect as clients. - */ -abstract class InterProcessCommunicator implements InterProcessCommunicationProtocol, Closeable { - - private static final Logger LOG = LoggerFactory.getLogger(InterProcessCommunicator.class); - private static final String RMI_NAME = "Cryptomator"; - - public abstract boolean isServer(); - - /** - * @param portFilePath Path to a file containing the IPC port - * @param endpoint The server-side communication endpoint. - * @return Either a client or a server communicator. - * @throws IOException In case of communication errors. - */ - public static InterProcessCommunicator start(Path portFilePath, InterProcessCommunicationProtocol endpoint) throws IOException { - System.setProperty("java.rmi.server.hostname", "localhost"); - try { - // try to connect to existing server: - ClientCommunicator client = new ClientCommunicator(portFilePath); - LOG.trace("Connected to running process."); - return client; - } catch (ConnectException | ConnectIOException | NotBoundException e) { - LOG.debug("Could not connect to running process."); - // continue - } - - // spawn a new server: - LOG.trace("Spawning new server..."); - ServerCommunicator server = new ServerCommunicator(endpoint, portFilePath); - LOG.debug("Server listening on port {}.", server.getPort()); - return server; - } - - public static class ClientCommunicator extends InterProcessCommunicator { - - private final IpcProtocolRemote remote; - - private ClientCommunicator(Path portFilePath) throws ConnectException, NotBoundException, RemoteException { - if (Files.notExists(portFilePath)) { - throw new ConnectException("No IPC port file."); - } - try { - int port = ClientCommunicator.readPort(portFilePath); - LOG.debug("Connecting to port {}...", port); - Registry registry = LocateRegistry.getRegistry("localhost", port, new ClientSocketFactory()); - this.remote = (IpcProtocolRemote) registry.lookup(RMI_NAME); - } catch (IOException e) { - throw new ConnectException("Error reading IPC port file."); - } - } - - private static int readPort(Path portFilePath) throws IOException { - ByteBuffer buf = ByteBuffer.allocate(Integer.BYTES); - try (ReadableByteChannel ch = Files.newByteChannel(portFilePath, StandardOpenOption.READ)) { - if (ch.read(buf) == Integer.BYTES) { - buf.flip(); - return buf.getInt(); - } else { - throw new IOException("Invalid IPC port file."); - } - } - } - - @Override - public void handleLaunchArgs(String[] args) { - try { - remote.handleLaunchArgs(args); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - @Override - public boolean isServer() { - return false; - } - - @Override - public void close() { - // no-op - } - - } - - public static class ServerCommunicator extends InterProcessCommunicator { - - private final ServerSocket socket; - private final Registry registry; - private final IpcProtocolRemoteImpl remote; - private final Path portFilePath; - - private ServerCommunicator(InterProcessCommunicationProtocol delegate, Path portFilePath) throws IOException { - this.socket = new ServerSocket(0, Byte.MAX_VALUE, InetAddress.getByName("localhost")); - RMIClientSocketFactory csf = RMISocketFactory.getDefaultSocketFactory(); - SingletonServerSocketFactory ssf = new SingletonServerSocketFactory(socket); - this.registry = LocateRegistry.createRegistry(0, csf, ssf); - this.remote = new IpcProtocolRemoteImpl(delegate); - UnicastRemoteObject.exportObject(remote, 0); - registry.rebind(RMI_NAME, remote); - this.portFilePath = portFilePath; - ServerCommunicator.writePort(portFilePath, socket.getLocalPort()); - } - - private static void writePort(Path portFilePath, int port) throws IOException { - ByteBuffer buf = ByteBuffer.allocate(Integer.BYTES); - buf.putInt(port); - buf.flip(); - MoreFiles.createParentDirectories(portFilePath); - try (WritableByteChannel ch = Files.newByteChannel(portFilePath, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { - if (ch.write(buf) != Integer.BYTES) { - throw new IOException("Did not write expected number of bytes."); - } - } - } - - @Override - public void handleLaunchArgs(String[] args) { - throw new UnsupportedOperationException("Server doesn't invoke methods."); - } - - @Override - public boolean isServer() { - return true; - } - - private int getPort() { - return socket.getLocalPort(); - } - - @Override - public void close() { - try { - registry.unbind(RMI_NAME); - UnicastRemoteObject.unexportObject(remote, true); - socket.close(); - Files.deleteIfExists(portFilePath); - LOG.debug("Server shut down."); - } catch (NotBoundException | IOException e) { - LOG.warn("Failed to close IPC Server.", e); - } - } - - } - - private static interface IpcProtocolRemote extends Remote { - void handleLaunchArgs(String[] args) throws RemoteException; - } - - private static class IpcProtocolRemoteImpl implements IpcProtocolRemote { - - private final InterProcessCommunicationProtocol delegate; - - protected IpcProtocolRemoteImpl(InterProcessCommunicationProtocol delegate) throws RemoteException { - this.delegate = delegate; - } - - @Override - public void handleLaunchArgs(String[] args) { - delegate.handleLaunchArgs(args); - } - - } - - /** - * Always returns the same pre-constructed server socket. - */ - private static class SingletonServerSocketFactory implements RMIServerSocketFactory { - - private final ServerSocket socket; - - public SingletonServerSocketFactory(ServerSocket socket) { - this.socket = socket; - } - - @Override - public synchronized ServerSocket createServerSocket(int port) throws IOException { - if (port != 0) { - throw new IllegalArgumentException("This factory doesn't support specific ports."); - } - return this.socket; - } - - } - - /** - * Creates client sockets with short timeouts. - */ - private static class ClientSocketFactory implements RMIClientSocketFactory { - - @Override - public Socket createSocket(String host, int port) throws IOException { - return new SocketWithFixedTimeout(host, port, 1000); - } - - } - - private static class SocketWithFixedTimeout extends Socket { - - public SocketWithFixedTimeout(String host, int port, int timeoutInMs) throws UnknownHostException, IOException { - super(host, port); - super.setSoTimeout(timeoutInMs); - } - - @Override - public synchronized void setSoTimeout(int timeout) throws SocketException { - // do nothing, timeout is fixed - } - - } - -} diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/IpcFactory.java b/main/launcher/src/main/java/org/cryptomator/launcher/IpcFactory.java new file mode 100644 index 000000000..112050d26 --- /dev/null +++ b/main/launcher/src/main/java/org/cryptomator/launcher/IpcFactory.java @@ -0,0 +1,258 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ +package org.cryptomator.launcher; + +import com.google.common.io.MoreFiles; +import org.cryptomator.common.Environment; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.io.Closeable; +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.WritableByteChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.rmi.NotBoundException; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; +import java.rmi.server.RMIClientSocketFactory; +import java.rmi.server.RMIServerSocketFactory; +import java.rmi.server.RMISocketFactory; +import java.rmi.server.UnicastRemoteObject; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * First running application on a machine opens a server socket. Further processes will connect as clients. + */ +@Singleton +class IpcFactory { + + private static final Logger LOG = LoggerFactory.getLogger(IpcFactory.class); + private static final String RMI_NAME = "Cryptomator"; + + private final List portFilePaths; + private final IpcProtocolImpl ipcHandler; + + @Inject + public IpcFactory(Environment env, IpcProtocolImpl ipcHandler) { + this.portFilePaths = env.getIpcPortPath().collect(Collectors.toUnmodifiableList()); + this.ipcHandler = ipcHandler; + } + + public IpcEndpoint create() { + if (portFilePaths.isEmpty()) { + LOG.warn("No IPC port file path specified."); + return new SelfEndpoint(ipcHandler); + } else { + System.setProperty("java.rmi.server.hostname", "localhost"); + return attemptClientConnection().or(this::createServerEndpoint).orElseGet(() -> new SelfEndpoint(ipcHandler)); + } + } + + private Optional attemptClientConnection() { + for (Path portFilePath : portFilePaths) { + try { + int port = readPort(portFilePath); + LOG.debug("[Client] Connecting to port {}...", port); + Registry registry = LocateRegistry.getRegistry("localhost", port, new ClientSocketFactory()); + IpcProtocol remoteInterface = (IpcProtocol) registry.lookup(RMI_NAME); + return Optional.of(new ClientEndpoint(remoteInterface)); + } catch (NotBoundException | IOException e) { + LOG.debug("[Client] Failed to connect."); + // continue with next portFilePath... + } + } + return Optional.empty(); + } + + private int readPort(Path portFilePath) throws IOException { + try (ReadableByteChannel ch = Files.newByteChannel(portFilePath, StandardOpenOption.READ)) { + LOG.debug("[Client] Reading IPC port from {}", portFilePath); + ByteBuffer buf = ByteBuffer.allocate(Integer.BYTES); + if (ch.read(buf) == Integer.BYTES) { + buf.flip(); + return buf.getInt(); + } else { + throw new IOException("Invalid IPC port file."); + } + } + } + + private Optional createServerEndpoint() { + assert !portFilePaths.isEmpty(); + Path portFilePath = portFilePaths.get(0); + try { + ServerSocket socket = new ServerSocket(0, Byte.MAX_VALUE, InetAddress.getByName("localhost")); + RMIClientSocketFactory csf = RMISocketFactory.getDefaultSocketFactory(); + SingletonServerSocketFactory ssf = new SingletonServerSocketFactory(socket); + Registry registry = LocateRegistry.createRegistry(0, csf, ssf); + UnicastRemoteObject.exportObject(ipcHandler, 0); + registry.rebind(RMI_NAME, ipcHandler); + writePort(portFilePath, socket.getLocalPort()); + return Optional.of(new ServerEndpoint(ipcHandler, socket, registry, portFilePath)); + } catch (IOException e) { + LOG.warn("[Server] Failed to create IPC server.", e); + return Optional.empty(); + } + } + + private void writePort(Path portFilePath, int port) throws IOException { + ByteBuffer buf = ByteBuffer.allocate(Integer.BYTES); + buf.putInt(port); + buf.flip(); + MoreFiles.createParentDirectories(portFilePath); + try (WritableByteChannel ch = Files.newByteChannel(portFilePath, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { + if (ch.write(buf) != Integer.BYTES) { + throw new IOException("Did not write expected number of bytes."); + } + } + LOG.debug("[Server] Wrote IPC port {} to {}", port, portFilePath); + } + + interface IpcEndpoint extends Closeable { + + boolean isConnectedToRemote(); + + IpcProtocol getRemote(); + + } + + static class SelfEndpoint implements IpcEndpoint { + + protected final IpcProtocol remoteObject; + + SelfEndpoint(IpcProtocol remoteObject) { + this.remoteObject = remoteObject; + } + + @Override + public boolean isConnectedToRemote() { + return false; + } + + @Override + public IpcProtocol getRemote() { + return remoteObject; + } + + @Override + public void close() { + // no-op + } + } + + static class ClientEndpoint implements IpcEndpoint { + + private final IpcProtocol remoteInterface; + + public ClientEndpoint(IpcProtocol remoteInterface) { + this.remoteInterface = remoteInterface; + } + + public IpcProtocol getRemote() { + return remoteInterface; + } + + @Override + public boolean isConnectedToRemote() { + return true; + } + + @Override + public void close() { + // no-op + } + + } + + class ServerEndpoint extends SelfEndpoint { + + private final ServerSocket socket; + private final Registry registry; + private final Path portFilePath; + + private ServerEndpoint(IpcProtocol remoteObject, ServerSocket socket, Registry registry, Path portFilePath) { + super(remoteObject); + this.socket = socket; + this.registry = registry; + this.portFilePath = portFilePath; + } + + @Override + public void close() { + try { + registry.unbind(RMI_NAME); + UnicastRemoteObject.unexportObject(remoteObject, true); + socket.close(); + Files.deleteIfExists(portFilePath); + LOG.debug("[Server] Shut down"); + } catch (NotBoundException | IOException e) { + LOG.warn("[Server] Error shutting down:", e); + } + } + + } + + /** + * Always returns the same pre-constructed server socket. + */ + private static class SingletonServerSocketFactory implements RMIServerSocketFactory { + + private final ServerSocket socket; + + public SingletonServerSocketFactory(ServerSocket socket) { + this.socket = socket; + } + + @Override + public synchronized ServerSocket createServerSocket(int port) throws IOException { + if (port != 0) { + throw new IllegalArgumentException("This factory doesn't support specific ports."); + } + return this.socket; + } + + } + + /** + * Creates client sockets with short timeouts. + */ + private static class ClientSocketFactory implements RMIClientSocketFactory { + + @Override + public Socket createSocket(String host, int port) throws IOException { + return new SocketWithFixedTimeout(host, port, 1000); + } + + } + + private static class SocketWithFixedTimeout extends Socket { + + public SocketWithFixedTimeout(String host, int port, int timeoutInMs) throws UnknownHostException, IOException { + super(host, port); + super.setSoTimeout(timeoutInMs); + } + + @Override + public synchronized void setSoTimeout(int timeout) throws SocketException { + // do nothing, timeout is fixed + } + + } + +} diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/InterProcessCommunicationProtocol.java b/main/launcher/src/main/java/org/cryptomator/launcher/IpcProtocol.java similarity index 71% rename from main/launcher/src/main/java/org/cryptomator/launcher/InterProcessCommunicationProtocol.java rename to main/launcher/src/main/java/org/cryptomator/launcher/IpcProtocol.java index 1b3cc1fc8..40b4ded51 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/InterProcessCommunicationProtocol.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/IpcProtocol.java @@ -5,6 +5,11 @@ *******************************************************************************/ package org.cryptomator.launcher; -public interface InterProcessCommunicationProtocol { - void handleLaunchArgs(String[] args); +import java.rmi.Remote; +import java.rmi.RemoteException; + +interface IpcProtocol extends Remote { + + void handleLaunchArgs(String[] args) throws RemoteException; + } \ No newline at end of file diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/IpcProtocolImpl.java b/main/launcher/src/main/java/org/cryptomator/launcher/IpcProtocolImpl.java new file mode 100644 index 000000000..158ec290d --- /dev/null +++ b/main/launcher/src/main/java/org/cryptomator/launcher/IpcProtocolImpl.java @@ -0,0 +1,28 @@ +package org.cryptomator.launcher; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.Arrays; + +@Singleton +class IpcProtocolImpl implements IpcProtocol { + + private static final Logger LOG = LoggerFactory.getLogger(IpcProtocolImpl.class); + + private final FileOpenRequestHandler fileOpenRequestHandler; + + @Inject + public IpcProtocolImpl(FileOpenRequestHandler fileOpenRequestHandler) { + this.fileOpenRequestHandler = fileOpenRequestHandler; + } + + @Override + public void handleLaunchArgs(String[] args) { + LOG.info("Received launch args: {}", Arrays.stream(args).reduce((a, b) -> a + ", " + b).orElse("")); + fileOpenRequestHandler.handleLaunchArgs(args); + } + +} diff --git a/main/launcher/src/test/java/org/cryptomator/launcher/InterProcessCommunicatorTest.java b/main/launcher/src/test/java/org/cryptomator/launcher/InterProcessCommunicatorTest.java deleted file mode 100644 index 2b7d553b9..000000000 --- a/main/launcher/src/test/java/org/cryptomator/launcher/InterProcessCommunicatorTest.java +++ /dev/null @@ -1,102 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.launcher; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.SeekableByteChannel; -import java.nio.file.FileSystem; -import java.nio.file.NoSuchFileException; -import java.nio.file.Path; -import java.nio.file.attribute.BasicFileAttributes; -import java.nio.file.spi.FileSystemProvider; -import java.util.concurrent.atomic.AtomicInteger; - -public class InterProcessCommunicatorTest { - - Path portFilePath = Mockito.mock(Path.class); - Path portFileParentPath = Mockito.mock(Path.class); - BasicFileAttributes portFileParentPathAttrs = Mockito.mock(BasicFileAttributes.class); - FileSystem fs = Mockito.mock(FileSystem.class); - FileSystemProvider provider = Mockito.mock(FileSystemProvider.class); - SeekableByteChannel portFileChannel = Mockito.mock(SeekableByteChannel.class); - AtomicInteger port = new AtomicInteger(-1); - - @BeforeEach - public void setup() throws IOException { - Mockito.when(portFilePath.getFileSystem()).thenReturn(fs); - Mockito.when(portFilePath.toAbsolutePath()).thenReturn(portFilePath); - Mockito.when(portFilePath.normalize()).thenReturn(portFilePath); - Mockito.when(portFilePath.getParent()).thenReturn(portFileParentPath); - Mockito.when(portFileParentPath.getFileSystem()).thenReturn(fs); - Mockito.when(fs.provider()).thenReturn(provider); - Mockito.when(provider.readAttributes(portFileParentPath, BasicFileAttributes.class)).thenReturn(portFileParentPathAttrs); - Mockito.when(portFileParentPathAttrs.isDirectory()).thenReturn(false, true); // Guava's MoreFiles will check if dir exists before attempting to create them. - Mockito.when(provider.newByteChannel(Mockito.eq(portFilePath), Mockito.any(), Mockito.any())).thenReturn(portFileChannel); - Mockito.when(portFileChannel.read(Mockito.any())).then(invocation -> { - ByteBuffer buf = invocation.getArgument(0); - buf.putInt(port.get()); - return Integer.BYTES; - }); - Mockito.when(portFileChannel.write(Mockito.any())).then(invocation -> { - ByteBuffer buf = invocation.getArgument(0); - port.set(buf.getInt()); - return Integer.BYTES; - }); - } - - @Test - public void testStartWithDummyPort1() throws IOException { - port.set(0); - InterProcessCommunicationProtocol protocol = Mockito.mock(InterProcessCommunicationProtocol.class); - try (InterProcessCommunicator result = InterProcessCommunicator.start(portFilePath, protocol)) { - Assertions.assertTrue(result.isServer()); - Mockito.verify(provider).createDirectory(portFileParentPath); - Mockito.verifyZeroInteractions(protocol); - Assertions.assertThrows(UnsupportedOperationException.class, () -> { - result.handleLaunchArgs(new String[] {"foo"}); - }); - } - } - - @Test - public void testStartWithDummyPort2() throws IOException { - Mockito.doThrow(new NoSuchFileException("port file")).when(provider).checkAccess(portFilePath); - - InterProcessCommunicationProtocol protocol = Mockito.mock(InterProcessCommunicationProtocol.class); - try (InterProcessCommunicator result = InterProcessCommunicator.start(portFilePath, protocol)) { - Assertions.assertTrue(result.isServer()); - Mockito.verify(provider).createDirectory(portFileParentPath); - Mockito.verifyZeroInteractions(protocol); - } - } - - @Test - public void testInterProcessCommunication() throws IOException, InterruptedException { - port.set(-1); - InterProcessCommunicationProtocol protocol = Mockito.mock(InterProcessCommunicationProtocol.class); - try (InterProcessCommunicator result1 = InterProcessCommunicator.start(portFilePath, protocol)) { - Assertions.assertTrue(result1.isServer()); - Mockito.verify(provider, Mockito.times(1)).createDirectory(portFileParentPath); - Mockito.verifyZeroInteractions(protocol); - - try (InterProcessCommunicator result2 = InterProcessCommunicator.start(portFilePath, null)) { - Assertions.assertFalse(result2.isServer()); - Mockito.verify(provider, Mockito.times(1)).createDirectory(portFileParentPath); - Assertions.assertNotSame(result1, result2); - - result2.handleLaunchArgs(new String[] {"foo"}); - Mockito.verify(protocol).handleLaunchArgs(new String[] {"foo"}); - } - } - } - -} diff --git a/main/launcher/src/test/java/org/cryptomator/launcher/IpcFactoryTest.java b/main/launcher/src/test/java/org/cryptomator/launcher/IpcFactoryTest.java new file mode 100644 index 000000000..e4fef5556 --- /dev/null +++ b/main/launcher/src/test/java/org/cryptomator/launcher/IpcFactoryTest.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE file. + *******************************************************************************/ +package org.cryptomator.launcher; + +import org.cryptomator.common.Environment; +import org.cryptomator.launcher.IpcFactory.IpcEndpoint; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.Mockito; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; + +public class IpcFactoryTest { + + private Environment environment = Mockito.mock(Environment.class); + private IpcProtocolImpl protocolHandler = Mockito.mock(IpcProtocolImpl.class); + + @Test + @DisplayName("Wihout IPC port files") + public void testNoIpcWithoutPortFile() throws IOException { + IpcFactory inTest = new IpcFactory(environment, protocolHandler); + + Mockito.when(environment.getIpcPortPath()).thenReturn(Stream.empty()); + try (IpcEndpoint endpoint1 = inTest.create()) { + Assertions.assertEquals(IpcFactory.SelfEndpoint.class, endpoint1.getClass()); + Assertions.assertFalse(endpoint1.isConnectedToRemote()); + Assertions.assertSame(protocolHandler, endpoint1.getRemote()); + try (IpcEndpoint endpoint2 = inTest.create()) { + Assertions.assertEquals(IpcFactory.SelfEndpoint.class, endpoint2.getClass()); + Assertions.assertNotSame(endpoint1, endpoint2); + Assertions.assertFalse(endpoint2.isConnectedToRemote()); + Assertions.assertSame(protocolHandler, endpoint2.getRemote()); + } + } + } + + @Test + @DisplayName("Start server and client with port shared via file") + public void testInterProcessCommunication(@TempDir Path tmpDir) throws IOException { + Path portFile = tmpDir.resolve("testPortFile"); + Mockito.when(environment.getIpcPortPath()).thenReturn(Stream.of(portFile)); + IpcFactory inTest = new IpcFactory(environment, protocolHandler); + + Assertions.assertFalse(Files.exists(portFile)); + try (IpcEndpoint endpoint1 = inTest.create()) { + Assertions.assertEquals(IpcFactory.ServerEndpoint.class, endpoint1.getClass()); + Assertions.assertFalse(endpoint1.isConnectedToRemote()); + Assertions.assertTrue(Files.exists(portFile)); + Assertions.assertSame(protocolHandler, endpoint1.getRemote()); + Mockito.verifyZeroInteractions(protocolHandler); + try (IpcEndpoint endpoint2 = inTest.create()) { + Assertions.assertEquals(IpcFactory.ClientEndpoint.class, endpoint2.getClass()); + Assertions.assertNotSame(endpoint1, endpoint2); + Assertions.assertTrue(endpoint2.isConnectedToRemote()); + Assertions.assertNotSame(protocolHandler, endpoint2.getRemote()); + Mockito.verifyZeroInteractions(protocolHandler); + endpoint2.getRemote().handleLaunchArgs(new String[] {"foo"}); + Mockito.verify(protocolHandler).handleLaunchArgs(new String[] {"foo"}); + } + } + } + +} From 98e5c3ff883c9f34b6df8008fc17bc0382f6d152 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Wed, 20 Feb 2019 15:39:00 +0100 Subject: [PATCH 14/44] simplified handling of fileOpenRequests --- .../ui/controllers/MainController.java | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java index 3a0092804..c66712818 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java @@ -43,6 +43,7 @@ import javafx.scene.layout.Pane; import javafx.scene.text.Font; import javafx.stage.FileChooser; import javafx.stage.Stage; +import javafx.util.Duration; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.FxApplicationScoped; import org.cryptomator.common.settings.VaultSettings; @@ -56,6 +57,7 @@ import org.cryptomator.ui.model.Vault; import org.cryptomator.ui.model.VaultFactory; import org.cryptomator.ui.model.VaultList; import org.cryptomator.ui.util.DialogBuilderUtil; +import org.cryptomator.ui.util.Tasks; import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.Subscription; import org.fxmisc.easybind.monadic.MonadicBinding; @@ -247,22 +249,13 @@ public class MainController implements ViewController { } private void listenToFileOpenRequests(Stage stage) { - executorService.submit(() -> { - while (!Thread.interrupted()) { - try { - final Path path = fileOpenRequests.take(); - Platform.runLater(() -> { - addVault(path, true); - stage.setIconified(false); - stage.show(); - stage.toFront(); - stage.requestFocus(); - }); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - }); + Tasks.create(fileOpenRequests::take).onSuccess(path -> { + addVault(path, true); + stage.setIconified(false); + stage.show(); + stage.toFront(); + stage.requestFocus(); + }).schedulePeriodically(executorService, Duration.ZERO, Duration.ZERO); } private ListCell createDirecoryListCell(ListView param) { From 8814372c68a75b9190ac4b4758a3366dfbd2efee Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Wed, 20 Feb 2019 23:28:33 +0100 Subject: [PATCH 15/44] made Settings and DebugMode a Singleton --- .../main/java/org/cryptomator/launcher/Cryptomator.java | 8 +------- .../org/cryptomator/launcher/CryptomatorComponent.java | 3 +++ .../java/org/cryptomator/launcher/CryptomatorModule.java | 8 ++++++++ .../org/cryptomator/launcher/FxApplicationComponent.java | 2 -- .../src/main/java/org/cryptomator/logging/DebugMode.java | 3 ++- main/ui/src/main/java/org/cryptomator/ui/UiModule.java | 6 ------ 6 files changed, 14 insertions(+), 16 deletions(-) diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java b/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java index 12d1e5a65..9ad0e2b73 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java @@ -27,6 +27,7 @@ public class Cryptomator { if (endpoint.isConnectedToRemote()) { LOG.info("Found running application instance. Shutting down."); } else { + CRYPTOMATOR_COMPONENT.debugMode().initialize(); CleanShutdownPerformer.registerShutdownHook(); Application.launch(MainApp.class, args); } @@ -43,12 +44,9 @@ public class Cryptomator { // We need a separate FX Application class, until we can use the module system. See https://stackoverflow.com/q/54756176/4014509 public static class MainApp extends Application { - private Stage primaryStage; - @Override public void start(Stage primaryStage) { LOG.info("JavaFX application started."); - this.primaryStage = primaryStage; primaryStage.setMinWidth(652.0); primaryStage.setMinHeight(440.0); @@ -57,8 +55,6 @@ public class Cryptomator { .mainWindow(primaryStage) // .build(); - fxApplicationComponent.debugMode().initialize(); - MainController mainCtrl = fxApplicationComponent.fxmlLoader().load("/fxml/main.fxml"); mainCtrl.initStage(primaryStage); primaryStage.show(); @@ -66,8 +62,6 @@ public class Cryptomator { @Override public void stop() { - assert primaryStage != null; - primaryStage.hide(); LOG.info("JavaFX application stopped."); } diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java index eaedf7585..cc8acaf7d 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java @@ -3,6 +3,7 @@ package org.cryptomator.launcher; import dagger.Component; import org.cryptomator.common.CommonsModule; import org.cryptomator.common.Environment; +import org.cryptomator.logging.DebugMode; import javax.inject.Named; import javax.inject.Singleton; @@ -12,6 +13,8 @@ import java.util.Optional; @Component(modules = {CryptomatorModule.class, CommonsModule.class}) public interface CryptomatorComponent { + DebugMode debugMode(); + IpcFactory ipcFactory(); @Named("applicationVersion") diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java index 0efe18499..4c9971471 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java @@ -2,6 +2,8 @@ package org.cryptomator.launcher; import dagger.Module; import dagger.Provides; +import org.cryptomator.common.settings.Settings; +import org.cryptomator.common.settings.SettingsProvider; import javax.inject.Named; import javax.inject.Singleton; @@ -13,6 +15,12 @@ import java.util.concurrent.BlockingQueue; @Module class CryptomatorModule { + @Provides + @Singleton + Settings provideSettings(SettingsProvider settingsProvider) { + return settingsProvider.get(); + } + @Provides @Singleton @Named("fileOpenRequests") diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/FxApplicationComponent.java b/main/launcher/src/main/java/org/cryptomator/launcher/FxApplicationComponent.java index d6985bf88..701e3e546 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/FxApplicationComponent.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/FxApplicationComponent.java @@ -21,8 +21,6 @@ interface FxApplicationComponent { ViewControllerLoader fxmlLoader(); - DebugMode debugMode(); - @Subcomponent.Builder interface Builder { diff --git a/main/launcher/src/main/java/org/cryptomator/logging/DebugMode.java b/main/launcher/src/main/java/org/cryptomator/logging/DebugMode.java index 7f18bd5c3..bf72627ad 100644 --- a/main/launcher/src/main/java/org/cryptomator/logging/DebugMode.java +++ b/main/launcher/src/main/java/org/cryptomator/logging/DebugMode.java @@ -14,11 +14,12 @@ import org.slf4j.ILoggerFactory; import org.slf4j.LoggerFactory; import javax.inject.Inject; +import javax.inject.Singleton; import java.util.Collection; import static java.util.Arrays.asList; -@FxApplicationScoped +@Singleton public class DebugMode { private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(DebugMode.class); diff --git a/main/ui/src/main/java/org/cryptomator/ui/UiModule.java b/main/ui/src/main/java/org/cryptomator/ui/UiModule.java index ee56d5a35..f1cbc1c00 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/UiModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/UiModule.java @@ -34,12 +34,6 @@ public class UiModule { private static final int NUM_SCHEDULER_THREADS = 4; - @Provides - @FxApplicationScoped - Settings provideSettings(SettingsProvider settingsProvider) { - return settingsProvider.get(); - } - @Provides @FxApplicationScoped ScheduledExecutorService provideScheduledExecutorService(@Named("shutdownTaskScheduler") Consumer shutdownTaskScheduler) { From debcab47e2c0bdcb39bb6231f401d8f1fef5bc91 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Thu, 21 Feb 2019 00:02:12 +0100 Subject: [PATCH 16/44] Refactored "file open events" to "app launch events", fixes #55 --- .../launcher/CryptomatorModule.java | 6 +- .../launcher/FileOpenRequestHandler.java | 34 +++++--- .../launcher/FileOpenRequestHandlerTest.java | 79 +++++++++---------- .../ui/controllers/MainController.java | 11 +-- .../cryptomator/ui/model/AppLaunchEvent.java | 15 ++++ 5 files changed, 84 insertions(+), 61 deletions(-) create mode 100644 main/ui/src/main/java/org/cryptomator/ui/model/AppLaunchEvent.java diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java index 4c9971471..8b93a2f08 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java @@ -4,10 +4,10 @@ import dagger.Module; import dagger.Provides; import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.SettingsProvider; +import org.cryptomator.ui.model.AppLaunchEvent; import javax.inject.Named; import javax.inject.Singleton; -import java.nio.file.Path; import java.util.Optional; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; @@ -23,8 +23,8 @@ class CryptomatorModule { @Provides @Singleton - @Named("fileOpenRequests") - BlockingQueue provideFileOpenRequests() { + @Named("launchEventQueue") + BlockingQueue provideFileOpenRequests() { return new ArrayBlockingQueue<>(10); } diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java b/main/launcher/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java index 663f01b3c..535e2767c 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java @@ -13,8 +13,13 @@ import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.InvalidPathException; import java.nio.file.Path; +import java.util.Arrays; +import java.util.Objects; import java.util.concurrent.BlockingQueue; +import java.util.function.Function; +import java.util.stream.Stream; +import org.cryptomator.ui.model.AppLaunchEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,11 +31,11 @@ import javax.inject.Singleton; class FileOpenRequestHandler { private static final Logger LOG = LoggerFactory.getLogger(FileOpenRequestHandler.class); - private final BlockingQueue fileOpenRequests; + private final BlockingQueue launchEventQueue; @Inject - public FileOpenRequestHandler(@Named("fileOpenRequests") BlockingQueue fileOpenRequests) { - this.fileOpenRequests = fileOpenRequests; + public FileOpenRequestHandler(@Named("launchEventQueue") BlockingQueue launchEventQueue) { + this.launchEventQueue = launchEventQueue; try { Desktop.getDesktop().setOpenFileHandler(this::openFiles); } catch (UnsupportedOperationException e) { @@ -39,7 +44,9 @@ class FileOpenRequestHandler { } private void openFiles(final OpenFilesEvent evt) { - evt.getFiles().stream().map(File::toPath).forEach(fileOpenRequests::add); + Stream pathsToOpen = evt.getFiles().stream().map(File::toPath); + AppLaunchEvent launchEvent = new AppLaunchEvent(pathsToOpen); + tryToEnqueueFileOpenRequest(launchEvent); } public void handleLaunchArgs(String[] args) { @@ -48,19 +55,22 @@ class FileOpenRequestHandler { // visible for testing void handleLaunchArgs(FileSystem fs, String[] args) { - for (String arg : args) { + Stream pathsToOpen = Arrays.stream(args).map(str -> { try { - Path path = fs.getPath(arg); - tryToEnqueueFileOpenRequest(path); + return fs.getPath(str); } catch (InvalidPathException e) { - LOG.trace("{} not a valid path", arg); + LOG.trace("Argument not a valid path: {}", str); + return null; } - } + }).filter(Objects::nonNull); + AppLaunchEvent launchEvent = new AppLaunchEvent(pathsToOpen); + tryToEnqueueFileOpenRequest(launchEvent); } - private void tryToEnqueueFileOpenRequest(Path path) { - if (!fileOpenRequests.offer(path)) { - LOG.warn("{} could not be enqueued for opening.", path); + + private void tryToEnqueueFileOpenRequest(AppLaunchEvent launchEvent) { + if (!launchEventQueue.offer(launchEvent)) { + LOG.warn("Could not enqueue application launch event.", launchEvent); } } diff --git a/main/launcher/src/test/java/org/cryptomator/launcher/FileOpenRequestHandlerTest.java b/main/launcher/src/test/java/org/cryptomator/launcher/FileOpenRequestHandlerTest.java index 9bf8cd2be..673f23c6d 100644 --- a/main/launcher/src/test/java/org/cryptomator/launcher/FileOpenRequestHandlerTest.java +++ b/main/launcher/src/test/java/org/cryptomator/launcher/FileOpenRequestHandlerTest.java @@ -5,7 +5,13 @@ *******************************************************************************/ package org.cryptomator.launcher; +import org.cryptomator.ui.model.AppLaunchEvent; +import org.hamcrest.CoreMatchers; +import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -13,64 +19,55 @@ import java.io.IOException; import java.nio.file.FileSystem; import java.nio.file.InvalidPathException; import java.nio.file.Path; -import java.nio.file.attribute.BasicFileAttributes; -import java.nio.file.spi.FileSystemProvider; +import java.nio.file.Paths; +import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class FileOpenRequestHandlerTest { - @Test - public void testOpenArgsWithCorrectPaths() throws IOException { - Path p1 = Mockito.mock(Path.class); - Path p2 = Mockito.mock(Path.class); - FileSystem fs = Mockito.mock(FileSystem.class); - FileSystemProvider provider = Mockito.mock(FileSystemProvider.class); - BasicFileAttributes attrs = Mockito.mock(BasicFileAttributes.class); - Mockito.when(p1.getFileSystem()).thenReturn(fs); - Mockito.when(p2.getFileSystem()).thenReturn(fs); - Mockito.when(fs.provider()).thenReturn(provider); - Mockito.when(fs.getPath(Mockito.anyString())).thenReturn(p1, p2); - Mockito.when(provider.readAttributes(Mockito.any(), Mockito.eq(BasicFileAttributes.class))).thenReturn(attrs); + private FileOpenRequestHandler inTest; + private BlockingQueue queue; - BlockingQueue queue = new ArrayBlockingQueue<>(10); - FileOpenRequestHandler handler = new FileOpenRequestHandler(queue); - handler.handleLaunchArgs(fs, new String[] {"foo", "bar"}); - - Assertions.assertEquals(p1, queue.poll()); - Assertions.assertEquals(p2, queue.poll()); + @BeforeEach + public void setup() { + queue = new ArrayBlockingQueue<>(1); + inTest = new FileOpenRequestHandler(queue); } @Test + @DisplayName("./cryptomator.exe foo bar") + public void testOpenArgsWithCorrectPaths() throws IOException { + inTest.handleLaunchArgs(new String[]{"foo", "bar"}); + + AppLaunchEvent evt = queue.poll(); + Assertions.assertNotNull(evt); + List paths = evt.getPathsToOpen().collect(Collectors.toList()); + MatcherAssert.assertThat(paths, CoreMatchers.hasItems(Paths.get("foo"), Paths.get("bar"))); + } + + @Test + @DisplayName("./cryptomator.exe foo (with 'foo' being an invalid path)") public void testOpenArgsWithIncorrectPaths() throws IOException { FileSystem fs = Mockito.mock(FileSystem.class); - Mockito.when(fs.getPath(Mockito.anyString())).thenThrow(new InvalidPathException("foo", "foo is not a path")); + Mockito.when(fs.getPath("foo")).thenThrow(new InvalidPathException("foo", "foo is not a path")); + inTest.handleLaunchArgs(fs, new String[]{"foo"}); - @SuppressWarnings("unchecked") - BlockingQueue queue = Mockito.mock(BlockingQueue.class); - FileOpenRequestHandler handler = new FileOpenRequestHandler(queue); - handler.handleLaunchArgs(fs, new String[] {"foo"}); - - Mockito.verifyNoMoreInteractions(queue); + AppLaunchEvent evt = queue.poll(); + Assertions.assertNotNull(evt); + List paths = evt.getPathsToOpen().collect(Collectors.toList()); + Assertions.assertTrue(paths.isEmpty()); } @Test + @DisplayName("./cryptomator.exe foo (with full event queue)") public void testOpenArgsWithFullQueue() throws IOException { - Path p = Mockito.mock(Path.class); - FileSystem fs = Mockito.mock(FileSystem.class); - FileSystemProvider provider = Mockito.mock(FileSystemProvider.class); - BasicFileAttributes attrs = Mockito.mock(BasicFileAttributes.class); - Mockito.when(p.getFileSystem()).thenReturn(fs); - Mockito.when(fs.provider()).thenReturn(provider); - Mockito.when(fs.getPath(Mockito.anyString())).thenReturn(p); - Mockito.when(provider.readAttributes(Mockito.eq(p), Mockito.eq(BasicFileAttributes.class))).thenReturn(attrs); - Mockito.when(attrs.isRegularFile()).thenReturn(true); + queue.add(new AppLaunchEvent(Stream.empty())); + Assumptions.assumeTrue(queue.remainingCapacity() == 0); - @SuppressWarnings("unchecked") - BlockingQueue queue = Mockito.mock(BlockingQueue.class); - Mockito.when(queue.offer(Mockito.any())).thenReturn(false); - FileOpenRequestHandler handler = new FileOpenRequestHandler(queue); - handler.handleLaunchArgs(fs, new String[] {"foo"}); + inTest.handleLaunchArgs(new String[]{"foo"}); } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java index c66712818..7fc86867e 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java @@ -50,6 +50,7 @@ import org.cryptomator.common.settings.VaultSettings; import org.cryptomator.ui.ExitUtil; import org.cryptomator.ui.controls.DirectoryListCell; import org.cryptomator.ui.l10n.Localization; +import org.cryptomator.ui.model.AppLaunchEvent; import org.cryptomator.ui.model.AutoUnlocker; import org.cryptomator.ui.model.UpgradeStrategies; import org.cryptomator.ui.model.UpgradeStrategy; @@ -93,7 +94,7 @@ public class MainController implements ViewController { private final ExitUtil exitUtil; private final Localization localization; private final ExecutorService executorService; - private final BlockingQueue fileOpenRequests; + private final BlockingQueue launchEventQueue; private final VaultFactory vaultFactoy; private final ViewControllerLoader viewControllerLoader; private final ObjectProperty activeController = new SimpleObjectProperty<>(); @@ -110,11 +111,11 @@ public class MainController implements ViewController { private Subscription subs = Subscription.EMPTY; @Inject - public MainController(@Named("mainWindow") Stage mainWindow, ExecutorService executorService, @Named("fileOpenRequests") BlockingQueue fileOpenRequests, ExitUtil exitUtil, Localization localization, + public MainController(@Named("mainWindow") Stage mainWindow, ExecutorService executorService, @Named("launchEventQueue") BlockingQueue launchEventQueue, ExitUtil exitUtil, Localization localization, VaultFactory vaultFactoy, ViewControllerLoader viewControllerLoader, UpgradeStrategies upgradeStrategies, VaultList vaults, AutoUnlocker autoUnlocker) { this.mainWindow = mainWindow; this.executorService = executorService; - this.fileOpenRequests = fileOpenRequests; + this.launchEventQueue = launchEventQueue; this.exitUtil = exitUtil; this.localization = localization; this.vaultFactoy = vaultFactoy; @@ -249,12 +250,12 @@ public class MainController implements ViewController { } private void listenToFileOpenRequests(Stage stage) { - Tasks.create(fileOpenRequests::take).onSuccess(path -> { - addVault(path, true); + Tasks.create(launchEventQueue::take).onSuccess(event -> { stage.setIconified(false); stage.show(); stage.toFront(); stage.requestFocus(); + event.getPathsToOpen().forEach(path -> addVault(path, true)); }).schedulePeriodically(executorService, Duration.ZERO, Duration.ZERO); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/AppLaunchEvent.java b/main/ui/src/main/java/org/cryptomator/ui/model/AppLaunchEvent.java new file mode 100644 index 000000000..e01cf40da --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/model/AppLaunchEvent.java @@ -0,0 +1,15 @@ +package org.cryptomator.ui.model; + +import java.nio.file.Path; +import java.util.stream.Stream; + +public class AppLaunchEvent { + + private final Stream pathsToOpen; + + public AppLaunchEvent(Stream pathsToOpen) {this.pathsToOpen = pathsToOpen;} + + public Stream getPathsToOpen() { + return pathsToOpen; + } +} From b9a120b51b6626b2e4f2a5be0ae36b823be2ddd2 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Thu, 21 Feb 2019 11:39:56 +0100 Subject: [PATCH 17/44] internalized logback config, added -Dcryptomator.logDir=path/relative/to/home. external logback configuration can still be used via -Dlogback.configurationFile=/path/to/logback.xml --- .../org/cryptomator/common/Environment.java | 15 ++++++ .../cryptomator/common/EnvironmentTest.java | 23 ++++++++ main/launcher/pom.xml | 4 ++ main/launcher/src/main/resources/logback.xml | 52 +++++++++++++++++++ .../src/test/resources/logback-test.xml | 2 - main/pom.xml | 6 +++ 6 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 main/launcher/src/main/resources/logback.xml diff --git a/main/commons/src/main/java/org/cryptomator/common/Environment.java b/main/commons/src/main/java/org/cryptomator/common/Environment.java index b45b743f2..033698f01 100644 --- a/main/commons/src/main/java/org/cryptomator/common/Environment.java +++ b/main/commons/src/main/java/org/cryptomator/common/Environment.java @@ -9,6 +9,7 @@ import javax.inject.Inject; import javax.inject.Singleton; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Optional; import java.util.Spliterator; import java.util.Spliterators; import java.util.function.Predicate; @@ -29,6 +30,7 @@ public class Environment { LOG.debug("cryptomator.settingsPath: {}", System.getProperty("cryptomator.settingsPath")); LOG.debug("cryptomator.ipcPortPath: {}", System.getProperty("cryptomator.ipcPortPath")); LOG.debug("cryptomator.keychainPath: {}", System.getProperty("cryptomator.keychainPath")); + LOG.debug("cryptomator.logDir: {}", System.getProperty("cryptomator.logDir")); } public Stream getSettingsPath() { @@ -43,6 +45,19 @@ public class Environment { return getPaths("cryptomator.keychainPath"); } + public Optional getLogDir() { + return getPath("cryptomator.logDir") // + .filter(Predicate.not(Path::isAbsolute)) // property must be a relative path + .map(ABSOLUTE_HOME_DIR::resolve); // resolve relative path against HOME + } + + + private Optional getPath(String propertyName) { + String value = System.getProperty(propertyName); + return Optional.ofNullable(value).map(Paths::get); + } + + // visible for testing Stream getPaths(String propertyName) { Stream rawSettingsPaths = getRawList(propertyName, PATH_LIST_SEP); diff --git a/main/commons/src/test/java/org/cryptomator/common/EnvironmentTest.java b/main/commons/src/test/java/org/cryptomator/common/EnvironmentTest.java index 8fe899630..12b8ba28e 100644 --- a/main/commons/src/test/java/org/cryptomator/common/EnvironmentTest.java +++ b/main/commons/src/test/java/org/cryptomator/common/EnvironmentTest.java @@ -2,6 +2,7 @@ package org.cryptomator.common; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -11,6 +12,7 @@ import org.junit.jupiter.api.Test; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; @DisplayName("Environment Variables Test") @@ -60,6 +62,27 @@ class EnvironmentTest { MatcherAssert.assertThat(result, Matchers.contains(Paths.get("/home/testuser/AppData/Roaming/Cryptomator/keychain.json"))); } + @Test + @DisplayName("cryptomator.logDir=/foo/bar") + public void testAbsoluteLogDir() { + System.setProperty("cryptomator.logDir", "/foo/bar"); + + Optional logDir = env.getLogDir(); + + Assertions.assertFalse(logDir.isPresent()); + } + + @Test + @DisplayName("cryptomator.logDir=foo/bar") + public void testRelativeLogDir() { + System.setProperty("cryptomator.logDir", "foo/bar"); + + Optional logDir = env.getLogDir(); + + Assertions.assertTrue(logDir.isPresent()); + Assertions.assertEquals(Paths.get("/home/testuser/foo/bar"), logDir.get()); + } + @Nested @DisplayName("Path Lists") class SettingsPath { diff --git a/main/launcher/pom.xml b/main/launcher/pom.xml index aef980622..6677d1955 100644 --- a/main/launcher/pom.xml +++ b/main/launcher/pom.xml @@ -44,5 +44,9 @@ ch.qos.logback logback-classic + + org.codehaus.janino + janino + \ No newline at end of file diff --git a/main/launcher/src/main/resources/logback.xml b/main/launcher/src/main/resources/logback.xml new file mode 100644 index 000000000..46791f2dd --- /dev/null +++ b/main/launcher/src/main/resources/logback.xml @@ -0,0 +1,52 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + ${user.home}/Library/Logs/Cryptomator/cryptomator.log + + ${user.home}/${cryptomator.logDir}/cryptomator%i.log + 0 + 9 + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + ${user.home}/${cryptomator.logDir}/upgrade.log + true + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/main/launcher/src/test/resources/logback-test.xml b/main/launcher/src/test/resources/logback-test.xml index 043b6d20a..ebf1f5e7c 100644 --- a/main/launcher/src/test/resources/logback-test.xml +++ b/main/launcher/src/test/resources/logback-test.xml @@ -1,6 +1,4 @@ - - diff --git a/main/pom.xml b/main/pom.xml index add0f6161..5d98886e9 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -44,6 +44,7 @@ 1.7.25 1.2.3 + 3.0.12 5.4.0 2.24.0 @@ -161,6 +162,11 @@ logback-classic ${logback.version} + + org.codehaus.janino + janino + ${janino.version} + From 53b0d5cb9f83f9ef47495f036ea028a1def88f24 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Thu, 21 Feb 2019 13:38:30 +0100 Subject: [PATCH 18/44] Environment now being injected into WindowsProtectedKeychainAccess --- main/keychain/pom.xml | 5 + .../WindowsProtectedKeychainAccess.java | 115 +++++++++--------- .../WindowsProtectedKeychainAccessTest.java | 24 ++-- 3 files changed, 76 insertions(+), 68 deletions(-) diff --git a/main/keychain/pom.xml b/main/keychain/pom.xml index 8ae41d506..29471ed1d 100644 --- a/main/keychain/pom.xml +++ b/main/keychain/pom.xml @@ -10,6 +10,11 @@ System Keychain Access + + org.cryptomator + commons + + org.apache.commons commons-lang3 diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/WindowsProtectedKeychainAccess.java b/main/keychain/src/main/java/org/cryptomator/keychain/WindowsProtectedKeychainAccess.java index 57d9cdfa7..ce8c5744d 100644 --- a/main/keychain/src/main/java/org/cryptomator/keychain/WindowsProtectedKeychainAccess.java +++ b/main/keychain/src/main/java/org/cryptomator/keychain/WindowsProtectedKeychainAccess.java @@ -5,30 +5,6 @@ *******************************************************************************/ package org.cryptomator.keychain; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Reader; -import java.io.UncheckedIOException; -import java.io.Writer; -import java.lang.reflect.Type; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; - -import javax.inject.Inject; - import com.google.common.io.BaseEncoding; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -42,11 +18,36 @@ import com.google.gson.JsonSerializer; import com.google.gson.annotations.SerializedName; import com.google.gson.reflect.TypeToken; import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.common.Environment; import org.cryptomator.jni.WinDataProtection; import org.cryptomator.jni.WinFunctions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.inject.Inject; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.UncheckedIOException; +import java.io.Writer; +import java.lang.reflect.Type; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + import static java.nio.charset.StandardCharsets.UTF_8; class WindowsProtectedKeychainAccess implements KeychainAccessStrategy { @@ -57,24 +58,13 @@ class WindowsProtectedKeychainAccess implements KeychainAccessStrategy { .disableHtmlEscaping().create(); private final Optional winFunctions; - private final Path keychainPath; + private final List keychainPaths; private Map keychainEntries; @Inject - public WindowsProtectedKeychainAccess(Optional winFunctions) { + public WindowsProtectedKeychainAccess(Optional winFunctions, Environment environment) { this.winFunctions = winFunctions; - String keychainPathProperty = System.getProperty("cryptomator.keychainPath"); - if (keychainPathProperty == null) { - LOG.warn("Windows DataProtection module loaded, but no cryptomator.keychainPath property found."); - } - if (keychainPathProperty != null) { - if (keychainPathProperty.startsWith("~/")) { - keychainPathProperty = SystemUtils.USER_HOME + keychainPathProperty.substring(1); - } - this.keychainPath = FileSystems.getDefault().getPath(keychainPathProperty); - } else { - this.keychainPath = null; - } + this.keychainPaths = environment.getKeychainPath().collect(Collectors.toList()); } private WinDataProtection dataProtection() { @@ -124,7 +114,7 @@ class WindowsProtectedKeychainAccess implements KeychainAccessStrategy { @Override public boolean isSupported() { - return SystemUtils.IS_OS_WINDOWS && winFunctions.isPresent() && keychainPath != null; + return SystemUtils.IS_OS_WINDOWS && winFunctions.isPresent() && !keychainPaths.isEmpty(); } private byte[] generateSalt() { @@ -138,30 +128,44 @@ class WindowsProtectedKeychainAccess implements KeychainAccessStrategy { private void loadKeychainEntriesIfNeeded() { if (keychainEntries == null) { - loadKeychainEntries(); - } - assert keychainEntries != null; - } - - private void loadKeychainEntries() { - Type type = new TypeToken>() { - }.getType(); - try (InputStream in = Files.newInputStream(keychainPath, StandardOpenOption.READ); // - Reader reader = new InputStreamReader(in, UTF_8)) { - keychainEntries = GSON.fromJson(reader, type); - } catch (JsonParseException | NoSuchFileException e) { - LOG.info("Creating new keychain at path {}", keychainPath); - } catch (IOException e) { - throw new UncheckedIOException("Could not read keychain from path " + keychainPath, e); + for (Path keychainPath : keychainPaths) { + Optional> keychain = loadKeychainEntries(keychainPath); + if (keychain.isPresent()) { + keychainEntries = keychain.get(); + break; + } + } } if (keychainEntries == null) { + LOG.info("Unable to load existing keychain file, creating new keychain."); keychainEntries = new HashMap<>(); } } + private Optional> loadKeychainEntries(Path keychainPath) { + LOG.debug("Attempting to load keychain from {}", keychainPath); + Type type = new TypeToken>() { + }.getType(); + try (InputStream in = Files.newInputStream(keychainPath, StandardOpenOption.READ); // + Reader reader = new InputStreamReader(in, UTF_8)) { + return Optional.of(GSON.fromJson(reader, type)); + } catch (NoSuchFileException | JsonParseException e) { + return Optional.empty(); + } catch (IOException e) { + throw new UncheckedIOException("Could not read keychain from path " + keychainPath, e); + } + } + private void saveKeychainEntries() { + if (keychainPaths.isEmpty()) { + throw new IllegalStateException("Can't save keychain if no keychain path is specified."); + } + saveKeychainEntries(keychainPaths.get(0)); + } + + private void saveKeychainEntries(Path keychainPath) { try (OutputStream out = Files.newOutputStream(keychainPath, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); // - Writer writer = new OutputStreamWriter(out, UTF_8)) { + Writer writer = new OutputStreamWriter(out, UTF_8)) { GSON.toJson(keychainEntries, writer); } catch (IOException e) { throw new UncheckedIOException("Could not read keychain from path " + keychainPath, e); @@ -169,6 +173,7 @@ class WindowsProtectedKeychainAccess implements KeychainAccessStrategy { } private static class KeychainEntry { + @SerializedName("ciphertext") byte[] ciphertext; @SerializedName("salt") diff --git a/main/keychain/src/test/java/org/cryptomator/keychain/WindowsProtectedKeychainAccessTest.java b/main/keychain/src/test/java/org/cryptomator/keychain/WindowsProtectedKeychainAccessTest.java index 88c23ffa1..796634f5e 100644 --- a/main/keychain/src/test/java/org/cryptomator/keychain/WindowsProtectedKeychainAccessTest.java +++ b/main/keychain/src/test/java/org/cryptomator/keychain/WindowsProtectedKeychainAccessTest.java @@ -5,45 +5,42 @@ *******************************************************************************/ package org.cryptomator.keychain; +import org.cryptomator.common.Environment; import org.cryptomator.jni.WinDataProtection; import org.cryptomator.jni.WinFunctions; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.mockito.Mockito; import org.mockito.stubbing.Answer; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.util.Optional; +import java.util.stream.Stream; public class WindowsProtectedKeychainAccessTest { - private Path tmpFile; + private Path keychainPath; private WindowsProtectedKeychainAccess keychain; @BeforeEach - public void setup() throws IOException { - tmpFile = Files.createTempFile("unit-tests", ".tmp"); - System.setProperty("cryptomator.keychainPath", tmpFile.toAbsolutePath().normalize().toString()); + public void setup(@TempDir Path tempDir) throws IOException { + keychainPath = tempDir.resolve("keychainfile.tmp"); + Environment env = Mockito.mock(Environment.class); + Mockito.when(env.getKeychainPath()).thenReturn(Stream.of(keychainPath)); WinFunctions winFunctions = Mockito.mock(WinFunctions.class); WinDataProtection winDataProtection = Mockito.mock(WinDataProtection.class); Mockito.when(winFunctions.dataProtection()).thenReturn(winDataProtection); Answer answerReturningFirstArg = invocation -> ((byte[]) invocation.getArgument(0)).clone(); Mockito.when(winDataProtection.protect(Mockito.any(), Mockito.any())).thenAnswer(answerReturningFirstArg); Mockito.when(winDataProtection.unprotect(Mockito.any(), Mockito.any())).thenAnswer(answerReturningFirstArg); - keychain = new WindowsProtectedKeychainAccess(Optional.of(winFunctions)); - } - - @AfterEach - public void teardown() throws IOException { - Files.deleteIfExists(tmpFile); + keychain = new WindowsProtectedKeychainAccess(Optional.of(winFunctions), env); } @Test - public void testStoreAndLoad() { + public void testStoreAndLoad() throws IOException { String storedPw1 = "topSecret"; String storedPw2 = "bottomSecret"; keychain.storePassphrase("myPassword", storedPw1); @@ -54,6 +51,7 @@ public class WindowsProtectedKeychainAccessTest { Assertions.assertEquals(storedPw2, loadedPw2); keychain.deletePassphrase("myPassword"); Assertions.assertNull(keychain.loadPassphrase("myPassword")); + Assertions.assertNotNull(keychain.loadPassphrase("myOtherPassword")); Assertions.assertNull(keychain.loadPassphrase("nonExistingPassword")); } From 39f9da16f98919de21b17859273a47fa8ea0d683 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Thu, 21 Feb 2019 14:03:52 +0100 Subject: [PATCH 19/44] mount path is now configurable via -Dcryptomator.mountPointsDir and no longer hardcoded to ~/.Cryptomator or ~/Library/Application\ Support/Cryptomator fixes #710 --- .../main/java/org/cryptomator/common/Environment.java | 5 +++++ .../java/org/cryptomator/ui/model/FuseVolume.java | 11 +++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/main/commons/src/main/java/org/cryptomator/common/Environment.java b/main/commons/src/main/java/org/cryptomator/common/Environment.java index 033698f01..16cb14aca 100644 --- a/main/commons/src/main/java/org/cryptomator/common/Environment.java +++ b/main/commons/src/main/java/org/cryptomator/common/Environment.java @@ -31,6 +31,7 @@ public class Environment { LOG.debug("cryptomator.ipcPortPath: {}", System.getProperty("cryptomator.ipcPortPath")); LOG.debug("cryptomator.keychainPath: {}", System.getProperty("cryptomator.keychainPath")); LOG.debug("cryptomator.logDir: {}", System.getProperty("cryptomator.logDir")); + LOG.debug("cryptomator.mountPointsDir: {}", System.getProperty("cryptomator.mountPointsDir")); } public Stream getSettingsPath() { @@ -51,6 +52,10 @@ public class Environment { .map(ABSOLUTE_HOME_DIR::resolve); // resolve relative path against HOME } + public Optional getMountPointsDir() { + return getPath("cryptomator.mountPointsDir").map(this::replaceHomeDir); + } + private Optional getPath(String propertyName) { String value = System.getProperty(propertyName); diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/FuseVolume.java b/main/ui/src/main/java/org/cryptomator/ui/model/FuseVolume.java index b1d07894b..71868066f 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/FuseVolume.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/FuseVolume.java @@ -1,6 +1,7 @@ package org.cryptomator.ui.model; import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.common.Environment; import org.cryptomator.common.settings.VaultSettings; import org.cryptomator.cryptofs.CryptoFileSystem; import org.cryptomator.frontend.fuse.mount.CommandFailedException; @@ -25,21 +26,19 @@ import java.util.Optional; public class FuseVolume implements Volume { private static final Logger LOG = LoggerFactory.getLogger(FuseVolume.class); - - // TODO: dont use fixed Strings and rather set them in some system environment variables in the cryptomator installer and load those! - private static final String DEFAULT_MOUNTROOTPATH_MAC = System.getProperty("user.home") + "/Library/Application Support/Cryptomator"; - private static final String DEFAULT_MOUNTROOTPATH_LINUX = System.getProperty("user.home") + "/.Cryptomator"; private static final int MAX_TMPMOUNTPOINT_CREATION_RETRIES = 10; private final VaultSettings vaultSettings; + private final Environment environment; private Mount fuseMnt; private Path mountPoint; private boolean createdTemporaryMountPoint; @Inject - public FuseVolume(VaultSettings vaultSettings) { + public FuseVolume(VaultSettings vaultSettings, Environment environment) { this.vaultSettings = vaultSettings; + this.environment = environment; } @Override @@ -71,7 +70,7 @@ public class FuseVolume implements Volume { } private Path createTemporaryMountPoint() throws IOException { - Path parent = Paths.get(SystemUtils.IS_OS_MAC ? DEFAULT_MOUNTROOTPATH_MAC : DEFAULT_MOUNTROOTPATH_LINUX); + Path parent = environment.getMountPointsDir().orElseThrow(); String basename = vaultSettings.getId(); for (int i = 0; i < MAX_TMPMOUNTPOINT_CREATION_RETRIES; i++) { try { From be5fce0ee94a03b763914f1430ee59d6769cac82 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Thu, 21 Feb 2019 14:17:44 +0100 Subject: [PATCH 20/44] make sure the directory containing temporary mount points exists --- main/ui/src/main/java/org/cryptomator/ui/model/FuseVolume.java | 1 + 1 file changed, 1 insertion(+) diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/FuseVolume.java b/main/ui/src/main/java/org/cryptomator/ui/model/FuseVolume.java index 71868066f..d7728f96e 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/FuseVolume.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/FuseVolume.java @@ -71,6 +71,7 @@ public class FuseVolume implements Volume { private Path createTemporaryMountPoint() throws IOException { Path parent = environment.getMountPointsDir().orElseThrow(); + Files.createDirectories(parent); String basename = vaultSettings.getId(); for (int i = 0; i < MAX_TMPMOUNTPOINT_CREATION_RETRIES; i++) { try { From deded33da8047ed31703db8aa21000c431e899d9 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Thu, 21 Feb 2019 14:52:35 +0100 Subject: [PATCH 21/44] fixed hard-coded path in log config and refinded logging in settingsprovider --- .../org/cryptomator/common/settings/SettingsProvider.java | 5 ++++- main/launcher/src/main/resources/logback.xml | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/main/commons/src/main/java/org/cryptomator/common/settings/SettingsProvider.java b/main/commons/src/main/java/org/cryptomator/common/settings/SettingsProvider.java index f10014db0..3f4da5c45 100644 --- a/main/commons/src/main/java/org/cryptomator/common/settings/SettingsProvider.java +++ b/main/commons/src/main/java/org/cryptomator/common/settings/SettingsProvider.java @@ -19,6 +19,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.Optional; @@ -86,8 +87,10 @@ public class SettingsProvider implements Provider { } LOG.info("Settings loaded from {}", path); return Stream.of(settings); + } catch (NoSuchFileException e) { + return Stream.empty(); } catch (IOException e) { - LOG.info("Failed to load settings, creating new one."); + LOG.warn("Exception while loading settings from " + path, e); return Stream.empty(); } } diff --git a/main/launcher/src/main/resources/logback.xml b/main/launcher/src/main/resources/logback.xml index 46791f2dd..56d7eb6c7 100644 --- a/main/launcher/src/main/resources/logback.xml +++ b/main/launcher/src/main/resources/logback.xml @@ -10,10 +10,10 @@ - ${user.home}/Library/Logs/Cryptomator/cryptomator.log + ${user.home}/${cryptomator.logDir}/cryptomator0.log ${user.home}/${cryptomator.logDir}/cryptomator%i.log - 0 + 1 9 From 4bfd1e64336186cf1874ff7c269b96387f5d9fa0 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Fri, 22 Feb 2019 16:38:39 +0100 Subject: [PATCH 22/44] Improved SecPasswordField and added unit tests SecPasswordFields will now normalize any input to NFC on the fly. Any input typed into the password field will now get converted to NFC on-the-fly. This allows subsequent code to keep working on the CharSequence returned by getCharacters() without the need of additional Normalization. Affects #521 --- main/pom.xml | 6 + main/ui/pom.xml | 5 + .../ui/controls/SecPasswordField.java | 67 ++++++- .../ui/controls/SecPasswordFieldTest.java | 163 ++++++++++++++++++ 4 files changed, 234 insertions(+), 7 deletions(-) create mode 100644 main/ui/src/test/java/org/cryptomator/ui/controls/SecPasswordFieldTest.java diff --git a/main/pom.xml b/main/pom.xml index 5d98886e9..98916b6b7 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -221,6 +221,12 @@ hamcrest-all ${hamcrest.version} + + org.openjfx + javafx-swing + ${javafx.version} + test + diff --git a/main/ui/pom.xml b/main/ui/pom.xml index 710fe7c3f..1465683f0 100644 --- a/main/ui/pom.xml +++ b/main/ui/pom.xml @@ -109,5 +109,10 @@ 1.1 test + + org.openjfx + javafx-swing + test + diff --git a/main/ui/src/main/java/org/cryptomator/ui/controls/SecPasswordField.java b/main/ui/src/main/java/org/cryptomator/ui/controls/SecPasswordField.java index 8a6d0539e..960a7220a 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controls/SecPasswordField.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controls/SecPasswordField.java @@ -15,6 +15,8 @@ import javafx.scene.input.Dragboard; import javafx.scene.input.TransferMode; import java.nio.CharBuffer; +import java.text.Normalizer; +import java.text.Normalizer.Form; import java.util.Arrays; /** @@ -53,15 +55,38 @@ public class SecPasswordField extends PasswordField { event.consume(); } + /** + * Replaces a range of characters with the given text. + * The text will be normalized to NFC. + * + * @param start The starting index in the range, inclusive. This must be >= 0 and < the end. + * @param end The ending index in the range, exclusive. This is one-past the last character to + * delete (consistent with the String manipulation methods). This must be > the start, + * and <= the length of the text. + * @param text The text that is to replace the range. This must not be null. + * @implNote Internally calls {@link PasswordField#replaceText(int, int, String)} with a dummy String for visual purposes. + */ @Override public void replaceText(int start, int end, String text) { + String normalizedText = Normalizer.normalize(text, Form.NFC); int removed = end - start; - int added = text.length(); - this.length += added - removed; - growContentIfNeeded(); - text.getChars(0, text.length(), content, start); + int added = normalizedText.length(); + int delta = added - removed; - String placeholderString = Strings.repeat(PLACEHOLDER, text.length()); + // ensure sufficient content buffer size + int oldLength = length; + this.length += delta; + growContentIfNeeded(); + + // shift existing content + if (delta != 0 && start < oldLength) { + System.arraycopy(content, end, content, end + delta, oldLength - end); + } + + // copy new text to content buffer + normalizedText.getChars(0, normalizedText.length(), content, start); + + String placeholderString = Strings.repeat(PLACEHOLDER, normalizedText.length()); super.replaceText(start, end, placeholderString); } @@ -69,7 +94,7 @@ public class SecPasswordField extends PasswordField { if (length > content.length) { char[] newContent = new char[length + GROW_BUFFER_SIZE]; System.arraycopy(content, 0, newContent, 0, content.length); - swipe(); + swipe(content); this.content = newContent; } } @@ -80,6 +105,7 @@ public class SecPasswordField extends PasswordField { * @return A character sequence backed by the SecPasswordField's buffer (not a copy). * @implNote The CharSequence will not copy the backing char[]. * Therefore any mutation to the SecPasswordField's content will mutate or eventually swipe the returned CharSequence. + * @implSpec The CharSequence is usually in NFC representation (unless NFD-encoded char[] is set via {@link #setPassword(char[])}). * @see #swipe() */ @Override @@ -87,6 +113,28 @@ public class SecPasswordField extends PasswordField { return CharBuffer.wrap(content, 0, length); } + /** + * Convenience method wrapper for {@link #setPassword(char[])}. + * + * @param password + * @see #setPassword(char[]) + */ + public void setPassword(CharSequence password) { + char[] buf = new char[password.length()]; + for (int i = 0; i < password.length(); i++) { + buf[i] = password.charAt(i); + } + setPassword(buf); + Arrays.fill(buf, SWIPE_CHAR); + } + + /** + * Directly sets the content of this password field to a copy of the given password. + * No conversion whatsoever happens. If you want to normalize the unicode representation of the password, + * do it before calling this method. + * + * @param password + */ public void setPassword(char[] password) { swipe(); content = Arrays.copyOf(password, password.length); @@ -100,7 +148,12 @@ public class SecPasswordField extends PasswordField { * Destroys the stored password by overriding each character with a different character. */ public void swipe() { - Arrays.fill(content, SWIPE_CHAR); + swipe(content); + length = 0; + } + + private void swipe(char[] buffer) { + Arrays.fill(buffer, SWIPE_CHAR); } } diff --git a/main/ui/src/test/java/org/cryptomator/ui/controls/SecPasswordFieldTest.java b/main/ui/src/test/java/org/cryptomator/ui/controls/SecPasswordFieldTest.java new file mode 100644 index 000000000..0c5538892 --- /dev/null +++ b/main/ui/src/test/java/org/cryptomator/ui/controls/SecPasswordFieldTest.java @@ -0,0 +1,163 @@ +package org.cryptomator.ui.controls; + +import javafx.embed.swing.JFXPanel; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import javax.swing.SwingUtilities; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + + +class SecPasswordFieldTest { + + private SecPasswordField pwField = new SecPasswordField(); + + @BeforeAll + static void initJavaFx() throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + SwingUtilities.invokeLater(() -> { + new JFXPanel(); // initializes JavaFX environment + latch.countDown(); + }); + + if (!latch.await(5L, TimeUnit.SECONDS)) { + throw new ExceptionInInitializerError(); + } + } + + @Nested + @DisplayName("Content Update Events") + class TextChange { + + @Test + @DisplayName("\"ant\".append(\"eater\")") + public void append() { + pwField.setPassword("ant"); + pwField.appendText("eater"); + + Assertions.assertEquals("anteater", pwField.getCharacters().toString()); + } + + @Test + @DisplayName("\"eater\".insert(0, \"ant\")") + public void insert1() { + pwField.setPassword("eater"); + pwField.insertText(0, "ant"); + + Assertions.assertEquals("anteater", pwField.getCharacters().toString()); + } + + @Test + @DisplayName("\"anteater\".insert(3, \"b\")") + public void insert2() { + pwField.setPassword("anteater"); + pwField.insertText(3, "b"); + + Assertions.assertEquals("antbeater", pwField.getCharacters().toString()); + } + + @Test + @DisplayName("\"anteater\".delete(0, 3)") + public void delete1() { + pwField.setPassword("anteater"); + pwField.deleteText(0, 3); + + Assertions.assertEquals("eater", pwField.getCharacters().toString()); + } + + @Test + @DisplayName("\"anteater\".delete(3, 8)") + public void delete2() { + pwField.setPassword("anteater"); + pwField.deleteText(3, 8); + + Assertions.assertEquals("ant", pwField.getCharacters().toString()); + } + + @Test + @DisplayName("\"anteater\".replace(0, 3, \"hand\")") + public void replace1() { + pwField.setPassword("anteater"); + pwField.replaceText(0, 3, "hand"); + + Assertions.assertEquals("handeater", pwField.getCharacters().toString()); + } + + @Test + @DisplayName("\"anteater\".replace(3, 6, \"keep\")") + public void replace2() { + pwField.setPassword("anteater"); + pwField.replaceText(3, 6, "keep"); + + Assertions.assertEquals("antkeeper", pwField.getCharacters().toString()); + } + + @Test + @DisplayName("\"anteater\".replace(0, 3, \"bee\")") + public void replace3() { + pwField.setPassword("anteater"); + pwField.replaceText(0, 3, "bee"); + + Assertions.assertEquals("beeeater", pwField.getCharacters().toString()); + } + + } + + @Test + @DisplayName("entering NFC string leads to NFC char[]") + public void enterNfcString() { + pwField.appendText("str\u00F6m"); // ström + pwField.insertText(0, "\u212Bng"); // Ång + pwField.appendText("\uD83D\uDCA9"); // 💩 + + CharSequence result = pwField.getCharacters(); + Assertions.assertEquals('\u00C5', result.charAt(0)); + Assertions.assertEquals('n', result.charAt(1)); + Assertions.assertEquals('g', result.charAt(2)); + Assertions.assertEquals('s', result.charAt(3)); + Assertions.assertEquals('t', result.charAt(4)); + Assertions.assertEquals('r', result.charAt(5)); + Assertions.assertEquals('ö', result.charAt(6)); + Assertions.assertEquals('m', result.charAt(7)); + Assertions.assertEquals('\uD83D', result.charAt(8)); + Assertions.assertEquals('\uDCA9', result.charAt(9)); + } + + @Test + @DisplayName("entering NFD string leads to NFC char[]") + public void enterNfdString() { + pwField.appendText("str\u006F\u0308m"); // ström + pwField.insertText(0, "\u0041\u030Ang"); // Ång + pwField.appendText("\uD83D\uDCA9"); // 💩 + + CharSequence result = pwField.getCharacters(); + Assertions.assertEquals('\u00C5', result.charAt(0)); + Assertions.assertEquals('n', result.charAt(1)); + Assertions.assertEquals('g', result.charAt(2)); + Assertions.assertEquals('s', result.charAt(3)); + Assertions.assertEquals('t', result.charAt(4)); + Assertions.assertEquals('r', result.charAt(5)); + Assertions.assertEquals('ö', result.charAt(6)); + Assertions.assertEquals('m', result.charAt(7)); + Assertions.assertEquals('\uD83D', result.charAt(8)); + Assertions.assertEquals('\uDCA9', result.charAt(9)); + } + + @Test + @DisplayName("test swipe char[]") + public void swipe() { + pwField.appendText("topSecret"); + + CharSequence result1 = pwField.getCharacters(); + Assertions.assertEquals("topSecret", result1.toString()); + pwField.swipe(); + CharSequence result2 = pwField.getCharacters(); + Assertions.assertEquals(" ", result1.toString()); + Assertions.assertEquals("", result2.toString()); + } + +} From cf020e5b9610afc5630514069e67570e1f27e86a Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Fri, 22 Feb 2019 16:50:05 +0100 Subject: [PATCH 23/44] disable UI control tests on headless systems --- .../java/org/cryptomator/ui/controls/SecPasswordFieldTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/main/ui/src/test/java/org/cryptomator/ui/controls/SecPasswordFieldTest.java b/main/ui/src/test/java/org/cryptomator/ui/controls/SecPasswordFieldTest.java index 0c5538892..34fac228f 100644 --- a/main/ui/src/test/java/org/cryptomator/ui/controls/SecPasswordFieldTest.java +++ b/main/ui/src/test/java/org/cryptomator/ui/controls/SecPasswordFieldTest.java @@ -2,12 +2,14 @@ package org.cryptomator.ui.controls; import javafx.embed.swing.JFXPanel; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import javax.swing.SwingUtilities; +import java.awt.GraphicsEnvironment; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -18,6 +20,7 @@ class SecPasswordFieldTest { @BeforeAll static void initJavaFx() throws InterruptedException { + Assumptions.assumeFalse(GraphicsEnvironment.isHeadless()); final CountDownLatch latch = new CountDownLatch(1); SwingUtilities.invokeLater(() -> { new JFXPanel(); // initializes JavaFX environment From d06720838e22a7535410661c3391ab5f56219821 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Sat, 23 Feb 2019 03:43:06 +0100 Subject: [PATCH 24/44] Logback configuration is now done programmatically, fixes #832 --- .../org/cryptomator/common/Environment.java | 6 +- main/launcher/pom.xml | 4 - .../org/cryptomator/launcher/Cryptomator.java | 12 +- .../launcher/CryptomatorComponent.java | 6 +- .../org/cryptomator/logging/LoggerModule.java | 149 ++++++++++++++++++ main/launcher/src/main/resources/logback.xml | 52 ------ .../src/test/resources/logback-test.xml | 18 --- main/pom.xml | 6 - 8 files changed, 165 insertions(+), 88 deletions(-) create mode 100644 main/launcher/src/main/java/org/cryptomator/logging/LoggerModule.java delete mode 100644 main/launcher/src/main/resources/logback.xml delete mode 100644 main/launcher/src/test/resources/logback-test.xml diff --git a/main/commons/src/main/java/org/cryptomator/common/Environment.java b/main/commons/src/main/java/org/cryptomator/common/Environment.java index 16cb14aca..7f42c9d1d 100644 --- a/main/commons/src/main/java/org/cryptomator/common/Environment.java +++ b/main/commons/src/main/java/org/cryptomator/common/Environment.java @@ -47,22 +47,18 @@ public class Environment { } public Optional getLogDir() { - return getPath("cryptomator.logDir") // - .filter(Predicate.not(Path::isAbsolute)) // property must be a relative path - .map(ABSOLUTE_HOME_DIR::resolve); // resolve relative path against HOME + return getPath("cryptomator.logDir").map(this::replaceHomeDir); } public Optional getMountPointsDir() { return getPath("cryptomator.mountPointsDir").map(this::replaceHomeDir); } - private Optional getPath(String propertyName) { String value = System.getProperty(propertyName); return Optional.ofNullable(value).map(Paths::get); } - // visible for testing Stream getPaths(String propertyName) { Stream rawSettingsPaths = getRawList(propertyName, PATH_LIST_SEP); diff --git a/main/launcher/pom.xml b/main/launcher/pom.xml index 6677d1955..aef980622 100644 --- a/main/launcher/pom.xml +++ b/main/launcher/pom.xml @@ -44,9 +44,5 @@ ch.qos.logback logback-classic - - org.codehaus.janino - janino - \ No newline at end of file diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java b/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java index 9ad0e2b73..95ef8ed3d 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java @@ -16,8 +16,16 @@ import java.io.IOException; public class Cryptomator { - private static final Logger LOG = LoggerFactory.getLogger(Cryptomator.class); - private static final CryptomatorComponent CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.create(); // DaggerCryptomatorComponent gets generated by Dagger. Run Maven and include target/generated-sources/annotations in your IDE. + private static final Logger LOG; + private static final CryptomatorComponent CRYPTOMATOR_COMPONENT; + + static { + // DaggerCryptomatorComponent gets generated by Dagger. + // Run Maven and include target/generated-sources/annotations in your IDE. + CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.create(); + CRYPTOMATOR_COMPONENT.initLogging().run(); + LOG = LoggerFactory.getLogger(Cryptomator.class); + } public static void main(String[] args) { LOG.info("Starting Cryptomator {} on {} {} ({})", CRYPTOMATOR_COMPONENT.applicationVersion().orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH); diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java index cc8acaf7d..068823744 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java @@ -4,15 +4,19 @@ import dagger.Component; import org.cryptomator.common.CommonsModule; import org.cryptomator.common.Environment; import org.cryptomator.logging.DebugMode; +import org.cryptomator.logging.LoggerModule; import javax.inject.Named; import javax.inject.Singleton; import java.util.Optional; @Singleton -@Component(modules = {CryptomatorModule.class, CommonsModule.class}) +@Component(modules = {CryptomatorModule.class, CommonsModule.class, LoggerModule.class}) public interface CryptomatorComponent { + @Named("initLogging") + Runnable initLogging(); + DebugMode debugMode(); IpcFactory ipcFactory(); diff --git a/main/launcher/src/main/java/org/cryptomator/logging/LoggerModule.java b/main/launcher/src/main/java/org/cryptomator/logging/LoggerModule.java new file mode 100644 index 000000000..429181d2e --- /dev/null +++ b/main/launcher/src/main/java/org/cryptomator/logging/LoggerModule.java @@ -0,0 +1,149 @@ +package org.cryptomator.logging; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.encoder.PatternLayoutEncoder; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.ConsoleAppender; +import ch.qos.logback.core.FileAppender; +import ch.qos.logback.core.helpers.NOPAppender; +import ch.qos.logback.core.hook.DelayingShutdownHook; +import ch.qos.logback.core.rolling.FixedWindowRollingPolicy; +import ch.qos.logback.core.rolling.RollingFileAppender; +import ch.qos.logback.core.util.Duration; +import dagger.Module; +import dagger.Provides; +import org.cryptomator.common.Environment; +import org.slf4j.ILoggerFactory; +import org.slf4j.LoggerFactory; + +import javax.inject.Named; +import javax.inject.Singleton; +import java.nio.file.Path; + +@Module +public class LoggerModule { + + private static final String UPGRADE_FILENAME = "upgrade.log"; + private static final String LOGFILE_NAME = "cryptomator0.log"; + private static final String LOGFILE_ROLLING_PATTERN = "cryptomator%i.log"; + private static final int LOGFILE_ROLLING_MIN = 1; + private static final int LOGFILE_ROLLING_MAX = 9; + private static final double SHUTDOWN_DELAY_MS = 100; + private static final Level ROOT_LOG_LEVEL = Level.INFO; + private static final String LOG_PATTERN = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"; + + @Provides + @Singleton + LoggerContext provideLoggerContext() { + ILoggerFactory loggerFactory = LoggerFactory.getILoggerFactory(); + if (loggerFactory instanceof LoggerContext) { + return (LoggerContext) loggerFactory; + } else { + throw new IllegalStateException("SLF4J not bound to Logback."); + } + } + + @Provides + @Singleton + PatternLayoutEncoder provideLayoutEncoder(LoggerContext context) { + PatternLayoutEncoder ple = new PatternLayoutEncoder(); + ple.setPattern(LOG_PATTERN); + ple.setContext(context); + ple.start(); + return ple; + } + + @Provides + @Singleton + @Named("stdoutAppender") + Appender provideStdoutAppender(LoggerContext context, PatternLayoutEncoder encoder) { + ConsoleAppender appender = new ConsoleAppender<>(); + appender.setContext(context); + appender.setEncoder(encoder); + appender.start(); + return appender; + } + + @Provides + @Singleton + @Named("fileAppender") + Appender provideFileAppender(LoggerContext context, PatternLayoutEncoder encoder, Environment environment) { + if (environment.getLogDir().isPresent()) { + Path logDir = environment.getLogDir().get(); + RollingFileAppender appender = new RollingFileAppender<>(); + appender.setContext(context); + appender.setFile(logDir.resolve(LOGFILE_NAME).toString()); + appender.setEncoder(encoder); + LaunchBasedTriggeringPolicy triggeringPolicy = new LaunchBasedTriggeringPolicy(); + triggeringPolicy.setContext(context); + triggeringPolicy.start(); + appender.setTriggeringPolicy(triggeringPolicy); + FixedWindowRollingPolicy rollingPolicy = new FixedWindowRollingPolicy(); + rollingPolicy.setContext(context); + rollingPolicy.setFileNamePattern(logDir.resolve(LOGFILE_ROLLING_PATTERN).toString()); + rollingPolicy.setMinIndex(LOGFILE_ROLLING_MIN); + rollingPolicy.setMaxIndex(LOGFILE_ROLLING_MAX); + rollingPolicy.setParent(appender); + rollingPolicy.start(); + appender.setRollingPolicy(rollingPolicy); + appender.start(); + return appender; + } else { + NOPAppender appender = new NOPAppender<>(); + appender.setContext(context); + return appender; + } + } + + @Provides + @Singleton + @Named("upgradeAppender") + Appender provideUpgradeAppender(LoggerContext context, PatternLayoutEncoder encoder, Environment environment) { + if (environment.getLogDir().isPresent()) { + FileAppender appender = new FileAppender<>(); + appender.setFile(environment.getLogDir().get().resolve(UPGRADE_FILENAME).toString()); + appender.setContext(context); + appender.setEncoder(encoder); + appender.start(); + return appender; + } else { + NOPAppender appender = new NOPAppender<>(); + appender.setContext(context); + return appender; + } + } + + @Provides + @Singleton + @Named("initLogging") + Runnable provideLogbackInitializer(LoggerContext context, // + @Named("stdoutAppender") Appender stdout, // + @Named("upgradeAppender") Appender upgrade, // + @Named("fileAppender") Appender file) { + return () -> { + context.reset(); + + // configure root logger: + Logger root = context.getLogger(Logger.ROOT_LOGGER_NAME); + root.setLevel(ROOT_LOG_LEVEL); + root.addAppender(stdout); + root.addAppender(file); + + // configure root logger: + Logger uprades = context.getLogger("org.cryptomator.ui.model"); + uprades.setLevel(Level.DEBUG); + uprades.addAppender(stdout); + uprades.addAppender(upgrade); + + // add shutdown hook + DelayingShutdownHook shutdownHook = new DelayingShutdownHook(); + shutdownHook.setContext(context); + shutdownHook.setDelay(Duration.buildByMilliseconds(SHUTDOWN_DELAY_MS)); + }; + } + + +} diff --git a/main/launcher/src/main/resources/logback.xml b/main/launcher/src/main/resources/logback.xml deleted file mode 100644 index 56d7eb6c7..000000000 --- a/main/launcher/src/main/resources/logback.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - ${user.home}/${cryptomator.logDir}/cryptomator0.log - - ${user.home}/${cryptomator.logDir}/cryptomator%i.log - 1 - 9 - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - ${user.home}/${cryptomator.logDir}/upgrade.log - true - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/main/launcher/src/test/resources/logback-test.xml b/main/launcher/src/test/resources/logback-test.xml deleted file mode 100644 index ebf1f5e7c..000000000 --- a/main/launcher/src/test/resources/logback-test.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - - - - - - diff --git a/main/pom.xml b/main/pom.xml index 98916b6b7..44efb705f 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -44,7 +44,6 @@ 1.7.25 1.2.3 - 3.0.12 5.4.0 2.24.0 @@ -162,11 +161,6 @@ logback-classic ${logback.version} - - org.codehaus.janino - janino - ${janino.version} - From 6adb591c9a01ef250fb1f561a8771e1702aa6dee Mon Sep 17 00:00:00 2001 From: Adrian Smith Date: Sun, 24 Feb 2019 17:22:31 +0000 Subject: [PATCH 25/44] Fix EnvironmentTest.testRelativeLogDir --- .../src/test/java/org/cryptomator/common/EnvironmentTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/commons/src/test/java/org/cryptomator/common/EnvironmentTest.java b/main/commons/src/test/java/org/cryptomator/common/EnvironmentTest.java index 12b8ba28e..36e3382d8 100644 --- a/main/commons/src/test/java/org/cryptomator/common/EnvironmentTest.java +++ b/main/commons/src/test/java/org/cryptomator/common/EnvironmentTest.java @@ -69,13 +69,13 @@ class EnvironmentTest { Optional logDir = env.getLogDir(); - Assertions.assertFalse(logDir.isPresent()); + Assertions.assertTrue(logDir.isPresent()); } @Test @DisplayName("cryptomator.logDir=foo/bar") public void testRelativeLogDir() { - System.setProperty("cryptomator.logDir", "foo/bar"); + System.setProperty("cryptomator.logDir", "~/foo/bar"); Optional logDir = env.getLogDir(); From 1048ff57287cace39f0cafb6fedf79728e832a38 Mon Sep 17 00:00:00 2001 From: Adrian Smith Date: Sun, 24 Feb 2019 21:01:04 +0000 Subject: [PATCH 26/44] Fix DisplayName on testRelativeLogDir --- .../src/test/java/org/cryptomator/common/EnvironmentTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/commons/src/test/java/org/cryptomator/common/EnvironmentTest.java b/main/commons/src/test/java/org/cryptomator/common/EnvironmentTest.java index 36e3382d8..cd8514397 100644 --- a/main/commons/src/test/java/org/cryptomator/common/EnvironmentTest.java +++ b/main/commons/src/test/java/org/cryptomator/common/EnvironmentTest.java @@ -73,7 +73,7 @@ class EnvironmentTest { } @Test - @DisplayName("cryptomator.logDir=foo/bar") + @DisplayName("cryptomator.logDir=~/foo/bar") public void testRelativeLogDir() { System.setProperty("cryptomator.logDir", "~/foo/bar"); From 129e9c63f82121b69c8234bb4a9ea38fc871dc50 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Mon, 25 Feb 2019 15:33:54 +0100 Subject: [PATCH 27/44] Clearer distinction between messageText and progressText in UnlockController --- .../ui/controllers/UnlockController.java | 15 ++++++++------- main/ui/src/main/resources/fxml/unlock.fxml | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java index cc6da7214..a9ab0049e 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java @@ -106,7 +106,7 @@ public class UnlockController implements ViewController { private Button unlockButton; @FXML - private Label successMessage; + private Text messageText; @FXML private CheckBox savePassword; @@ -136,7 +136,7 @@ public class UnlockController implements ViewController { private ProgressIndicator progressIndicator; @FXML - private Text messageText; + private Text progressText; @FXML private Hyperlink downloadsPageLink; @@ -200,8 +200,8 @@ public class UnlockController implements ViewController { advancedOptions.setVisible(false); advancedOptionsButton.setText(localization.getString("unlock.button.advancedOptions.show")); progressIndicator.setVisible(false); - successMessage.setVisible(state.successMessage().isPresent()); - state.successMessage().map(localization::getString).ifPresent(successMessage::setText); + progressText.setText(null); + state.successMessage().map(localization::getString).ifPresent(messageText::setText); if (SystemUtils.IS_OS_WINDOWS) { winDriveLetter.valueProperty().removeListener(driveLetterChangeListener); winDriveLetter.getItems().clear(); @@ -212,7 +212,6 @@ public class UnlockController implements ViewController { chooseSelectedDriveLetter(); } downloadsPageLink.setVisible(false); - messageText.setText(null); mountName.setText(vault.getMountName()); savePassword.setSelected(false); // auto-fill pw from keychain: @@ -298,7 +297,7 @@ public class UnlockController implements ViewController { @FXML private void didClickAdvancedOptionsButton(ActionEvent event) { - successMessage.setVisible(false); + messageText.setText(null); advancedOptions.setVisible(!advancedOptions.isVisible()); if (advancedOptions.isVisible()) { advancedOptionsButton.setText(localization.getString("unlock.button.advancedOptions.hide")); @@ -431,11 +430,12 @@ public class UnlockController implements ViewController { private void didClickUnlockButton(ActionEvent event) { advancedOptions.setDisable(true); advancedOptions.setVisible(false); + advancedOptionsButton.setText(localization.getString("unlock.button.advancedOptions.show")); progressIndicator.setVisible(true); CharSequence password = passwordField.getCharacters(); Tasks.create(() -> { - messageText.setText(localization.getString("unlock.pendingMessage.unlocking")); + progressText.setText(localization.getString("unlock.pendingMessage.unlocking")); vault.unlock(password); if (keychainAccess.isPresent() && savePassword.isSelected()) { keychainAccess.get().storePassphrase(vault.getId(), password); @@ -478,6 +478,7 @@ public class UnlockController implements ViewController { } advancedOptions.setDisable(false); progressIndicator.setVisible(false); + progressText.setText(null); }).runOnce(executor); } diff --git a/main/ui/src/main/resources/fxml/unlock.fxml b/main/ui/src/main/resources/fxml/unlock.fxml index 6439514ad..b64163b2a 100644 --- a/main/ui/src/main/resources/fxml/unlock.fxml +++ b/main/ui/src/main/resources/fxml/unlock.fxml @@ -43,7 +43,7 @@ - - org.openjfx - javafx-controls - ${javafx.version} + org.openjfx + javafx-controls + ${javafx.version} org.openjfx From dd3c969f0f409b5b075862b9eddead4b101faa4a Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Tue, 26 Feb 2019 22:41:33 +0100 Subject: [PATCH 35/44] FxApplication now part of the Dagger graph (references #663) --- .../org/cryptomator/launcher/Cryptomator.java | 131 +++++++++++------- .../launcher/CryptomatorComponent.java | 12 +- .../launcher/CryptomatorModule.java | 14 +- .../launcher/FileOpenRequestHandler.java | 1 + .../cryptomator/launcher/FxApplication.java | 40 ++++++ .../launcher/FxApplicationComponent.java | 22 +-- .../launcher/FxApplicationModule.java | 23 ++- .../logging/LoggerConfiguration.java | 71 ++++++++++ .../org/cryptomator/logging/LoggerModule.java | 52 +------ .../ui/controllers/MainController.java | 9 +- .../ui/controls/SecPasswordFieldTest.java | 6 +- 11 files changed, 242 insertions(+), 139 deletions(-) create mode 100644 main/launcher/src/main/java/org/cryptomator/launcher/FxApplication.java create mode 100644 main/launcher/src/main/java/org/cryptomator/logging/LoggerConfiguration.java diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java b/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java index 95ef8ed3d..7fd431bcb 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java @@ -5,74 +5,107 @@ *******************************************************************************/ package org.cryptomator.launcher; -import javafx.application.Application; -import javafx.stage.Stage; +import javafx.application.Platform; import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.ui.controllers.MainController; +import org.cryptomator.logging.DebugMode; +import org.cryptomator.logging.LoggerConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; import java.io.IOException; +import java.util.Optional; +import java.util.concurrent.CountDownLatch; +@Singleton public class Cryptomator { - private static final Logger LOG; - private static final CryptomatorComponent CRYPTOMATOR_COMPONENT; + // DaggerCryptomatorComponent gets generated by Dagger. + // Run Maven and include target/generated-sources/annotations in your IDE. + private static final CryptomatorComponent CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.create(); + private static final Logger LOG = LoggerFactory.getLogger(Cryptomator.class); - static { - // DaggerCryptomatorComponent gets generated by Dagger. - // Run Maven and include target/generated-sources/annotations in your IDE. - CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.create(); - CRYPTOMATOR_COMPONENT.initLogging().run(); - LOG = LoggerFactory.getLogger(Cryptomator.class); + private final LoggerConfiguration logConfig; + private final DebugMode debugMode; + private final IpcFactory ipcFactory; + private final Optional applicationVersion; + private final CountDownLatch shutdownLatch; + + @Inject + Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, IpcFactory ipcFactory, @Named("applicationVersion") Optional applicationVersion, @Named("shutdownLatch") CountDownLatch shutdownLatch) { + this.logConfig = logConfig; + this.debugMode = debugMode; + this.ipcFactory = ipcFactory; + this.applicationVersion = applicationVersion; + this.shutdownLatch = shutdownLatch; } public static void main(String[] args) { - LOG.info("Starting Cryptomator {} on {} {} ({})", CRYPTOMATOR_COMPONENT.applicationVersion().orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH); - - try (IpcFactory.IpcEndpoint endpoint = CRYPTOMATOR_COMPONENT.ipcFactory().create()) { - endpoint.getRemote().handleLaunchArgs(args); // if we are the server, getRemote() returns self. - if (endpoint.isConnectedToRemote()) { - LOG.info("Found running application instance. Shutting down."); - } else { - CRYPTOMATOR_COMPONENT.debugMode().initialize(); - CleanShutdownPerformer.registerShutdownHook(); - Application.launch(MainApp.class, args); - } - } catch (IOException e) { - LOG.error("Failed to initiate inter-process communication.", e); - System.exit(2); - } catch (Throwable e) { - LOG.error("Error during startup", e); - System.exit(1); - } - System.exit(0); // end remaining non-daemon threads. + int exitCode = CRYPTOMATOR_COMPONENT.application().run(args); + System.exit(exitCode); // end remaining non-daemon threads. } - // We need a separate FX Application class, until we can use the module system. See https://stackoverflow.com/q/54756176/4014509 - public static class MainApp extends Application { + /** + * Main entry point of the application launcher. + * @param args The arguments passed to this program via {@link #main(String[])}. + * @return Nonzero exit code in case of an error. + */ + private int run(String[] args) { + logConfig.init(); + LOG.info("Starting Cryptomator {} on {} {} ({})", applicationVersion.orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH); - @Override - public void start(Stage primaryStage) { - LOG.info("JavaFX application started."); - primaryStage.setMinWidth(652.0); - primaryStage.setMinHeight(440.0); - - FxApplicationComponent fxApplicationComponent = CRYPTOMATOR_COMPONENT.fxApplicationComponent() // - .fxApplication(this) // - .mainWindow(primaryStage) // - .build(); - - MainController mainCtrl = fxApplicationComponent.fxmlLoader().load("/fxml/main.fxml"); - mainCtrl.initStage(primaryStage); - primaryStage.show(); + if (sendArgsToRunningInstance(args)) { + LOG.info("Found running application instance. Shutting down..."); + return 0; } - @Override - public void stop() { - LOG.info("JavaFX application stopped."); + try { + runGuiApplication(); + LOG.info("Shutting down..."); + return 0; + } catch (Throwable e) { + LOG.error("Terminating due to error", e); + return 1; } + } + /** + * Attempts to create an IPC connection to a running Cryptomator instance and sends it the given args. + * If no external process could be reached, the args will be handled by the loopback IPC endpoint. + * + * @param args Arguments to send to the instance (if possible) + * @return true if a different process could be reached, false otherwise. + */ + private boolean sendArgsToRunningInstance(String[] args) { + try (IpcFactory.IpcEndpoint endpoint = ipcFactory.create()) { + endpoint.getRemote().handleLaunchArgs(args); // if we are the server, getRemote() returns self. + return endpoint.isConnectedToRemote(); + } catch (IOException e) { + LOG.error("Failed to initiate inter-process communication.", e); + return false; + } + } + + /** + * Launches the JavaFX application and waits until shutdown is requested. + * + * @implNote This method blocks until {@link #shutdownLatch} reached zero. + */ + private void runGuiApplication() { + try { + debugMode.initialize(); + CleanShutdownPerformer.registerShutdownHook(); + Platform.startup(() -> { + assert Platform.isFxApplicationThread(); + FxApplication app = CRYPTOMATOR_COMPONENT.fxApplicationComponent().application(); + app.start(); + }); + shutdownLatch.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } } } diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java index 068823744..480ff4c74 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java @@ -14,16 +14,8 @@ import java.util.Optional; @Component(modules = {CryptomatorModule.class, CommonsModule.class, LoggerModule.class}) public interface CryptomatorComponent { - @Named("initLogging") - Runnable initLogging(); + Cryptomator application(); - DebugMode debugMode(); - - IpcFactory ipcFactory(); - - @Named("applicationVersion") - Optional applicationVersion(); - - FxApplicationComponent.Builder fxApplicationComponent(); + FxApplicationComponent fxApplicationComponent(); } diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java index 8b93a2f08..2e7091acb 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java @@ -11,27 +11,35 @@ import javax.inject.Singleton; import java.util.Optional; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; @Module class CryptomatorModule { @Provides @Singleton - Settings provideSettings(SettingsProvider settingsProvider) { + @Named("shutdownLatch") + static CountDownLatch provideShutdownLatch() { + return new CountDownLatch(1); + } + + @Provides + @Singleton + static Settings provideSettings(SettingsProvider settingsProvider) { return settingsProvider.get(); } @Provides @Singleton @Named("launchEventQueue") - BlockingQueue provideFileOpenRequests() { + static BlockingQueue provideFileOpenRequests() { return new ArrayBlockingQueue<>(10); } @Provides @Singleton @Named("applicationVersion") - Optional provideApplicationVersion() { + static Optional provideApplicationVersion() { return Optional.ofNullable(Cryptomator.class.getPackage().getImplementationVersion()); } diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java b/main/launcher/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java index 535e2767c..c422b8b51 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java @@ -8,6 +8,7 @@ package org.cryptomator.launcher; import java.awt.Desktop; import java.awt.desktop.OpenFilesEvent; +import java.awt.desktop.QuitStrategy; import java.io.File; import java.nio.file.FileSystem; import java.nio.file.FileSystems; diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/FxApplication.java b/main/launcher/src/main/java/org/cryptomator/launcher/FxApplication.java new file mode 100644 index 000000000..e1b8b3a25 --- /dev/null +++ b/main/launcher/src/main/java/org/cryptomator/launcher/FxApplication.java @@ -0,0 +1,40 @@ +package org.cryptomator.launcher; + +import javafx.application.Application; +import javafx.stage.Stage; +import org.cryptomator.common.FxApplicationScoped; +import org.cryptomator.ui.controllers.MainController; +import org.cryptomator.ui.controllers.ViewControllerLoader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javax.inject.Named; + +@FxApplicationScoped +public class FxApplication extends Application { + + private static final Logger LOG = LoggerFactory.getLogger(FxApplication.class); + + private final Stage primaryStage; + private final ViewControllerLoader fxmlLoader; + + @Inject + FxApplication(@Named("mainWindow") Stage primaryStage, ViewControllerLoader fxmlLoader) { + this.primaryStage = primaryStage; + this.fxmlLoader = fxmlLoader; + } + + public void start() { + LOG.info("Starting GUI..."); + start(primaryStage); + } + + @Override + public void start(Stage primaryStage) { + MainController mainCtrl = fxmlLoader.load("/fxml/main.fxml"); + mainCtrl.initStage(primaryStage); + primaryStage.show(); + } + +} diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/FxApplicationComponent.java b/main/launcher/src/main/java/org/cryptomator/launcher/FxApplicationComponent.java index 701e3e546..ce37f7fa0 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/FxApplicationComponent.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/FxApplicationComponent.java @@ -5,33 +5,13 @@ *******************************************************************************/ package org.cryptomator.launcher; -import dagger.BindsInstance; import dagger.Subcomponent; -import javafx.application.Application; -import javafx.stage.Stage; import org.cryptomator.common.FxApplicationScoped; -import org.cryptomator.logging.DebugMode; -import org.cryptomator.ui.controllers.ViewControllerLoader; - -import javax.inject.Named; @FxApplicationScoped @Subcomponent(modules = FxApplicationModule.class) interface FxApplicationComponent { - ViewControllerLoader fxmlLoader(); - - @Subcomponent.Builder - interface Builder { - - @BindsInstance - Builder fxApplication(Application application); - - @BindsInstance - Builder mainWindow(@Named("mainWindow") Stage mainWindow); - - FxApplicationComponent build(); - - } + FxApplication application(); } diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/FxApplicationModule.java b/main/launcher/src/main/java/org/cryptomator/launcher/FxApplicationModule.java index 1d9988d06..95e2b25b1 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/FxApplicationModule.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/FxApplicationModule.java @@ -5,8 +5,12 @@ *******************************************************************************/ package org.cryptomator.launcher; +import dagger.Binds; import dagger.Module; import dagger.Provides; +import javafx.application.Application; +import javafx.application.Platform; +import javafx.stage.Stage; import org.cryptomator.common.FxApplicationScoped; import org.cryptomator.ui.UiModule; @@ -14,13 +18,28 @@ import javax.inject.Named; import java.util.function.Consumer; @Module(includes = {UiModule.class}) -class FxApplicationModule { +abstract class FxApplicationModule { @Provides @FxApplicationScoped @Named("shutdownTaskScheduler") - Consumer provideShutdownTaskScheduler() { + static Consumer provideShutdownTaskScheduler() { return CleanShutdownPerformer::scheduleShutdownTask; } + @Provides + @FxApplicationScoped + @Named("mainWindow") + static Stage providePrimaryStage() { + Stage stage = new Stage(); + stage.setMinWidth(652.0); + stage.setMinHeight(440.0); + return stage; + } + + @Binds + @FxApplicationScoped + abstract Application bindApplication(FxApplication application); + + } diff --git a/main/launcher/src/main/java/org/cryptomator/logging/LoggerConfiguration.java b/main/launcher/src/main/java/org/cryptomator/logging/LoggerConfiguration.java new file mode 100644 index 000000000..d1916abee --- /dev/null +++ b/main/launcher/src/main/java/org/cryptomator/logging/LoggerConfiguration.java @@ -0,0 +1,71 @@ +package org.cryptomator.logging; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.hook.DelayingShutdownHook; +import ch.qos.logback.core.util.Duration; +import org.cryptomator.common.Environment; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import java.util.Map; + +@Singleton +public class LoggerConfiguration { + + private static final double SHUTDOWN_DELAY_MS = 100; + + private final LoggerContext context; + private final Environment environment; + private final Appender stdout; + private final Appender upgrade; + private final Appender file; + + @Inject + LoggerConfiguration(LoggerContext context, // + Environment environment, // + @Named("stdoutAppender") Appender stdout, // + @Named("upgradeAppender") Appender upgrade, // + @Named("fileAppender") Appender file) { + this.context = context; + this.environment = environment; + this.stdout = stdout; + this.upgrade = upgrade; + this.file = file; + } + + public void init() { + if (environment.useCustomLogbackConfig()) { + Logger root = context.getLogger(Logger.ROOT_LOGGER_NAME); + root.info("Using external logback configuration file."); + } else { + context.reset(); + + // configure loggers: + for (Map.Entry loglevel : LoggerModule.DEFAULT_LOG_LEVELS.entrySet()) { + Logger logger = context.getLogger(loglevel.getKey()); + logger.setLevel(loglevel.getValue()); + logger.setAdditive(false); + logger.addAppender(stdout); + logger.addAppender(file); + } + + // configure upgrade logger: + Logger upgrades = context.getLogger("org.cryptomator.ui.model.upgrade"); + upgrades.setLevel(Level.DEBUG); + upgrades.addAppender(stdout); + upgrades.addAppender(upgrade); + upgrades.setAdditive(false); + + // add shutdown hook + DelayingShutdownHook shutdownHook = new DelayingShutdownHook(); + shutdownHook.setContext(context); + shutdownHook.setDelay(Duration.buildByMilliseconds(SHUTDOWN_DELAY_MS)); + } + } + +} diff --git a/main/launcher/src/main/java/org/cryptomator/logging/LoggerModule.java b/main/launcher/src/main/java/org/cryptomator/logging/LoggerModule.java index 1c0ef1bcc..dab3a3476 100644 --- a/main/launcher/src/main/java/org/cryptomator/logging/LoggerModule.java +++ b/main/launcher/src/main/java/org/cryptomator/logging/LoggerModule.java @@ -32,7 +32,6 @@ public class LoggerModule { private static final String LOGFILE_ROLLING_PATTERN = "cryptomator%i.log"; private static final int LOGFILE_ROLLING_MIN = 1; private static final int LOGFILE_ROLLING_MAX = 9; - private static final double SHUTDOWN_DELAY_MS = 100; private static final String LOG_PATTERN = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"; static final Map DEFAULT_LOG_LEVELS = Map.of( // Logger.ROOT_LOGGER_NAME, Level.INFO, // @@ -45,7 +44,7 @@ public class LoggerModule { @Provides @Singleton - LoggerContext provideLoggerContext() { + static LoggerContext provideLoggerContext() { ILoggerFactory loggerFactory = LoggerFactory.getILoggerFactory(); if (loggerFactory instanceof LoggerContext) { return (LoggerContext) loggerFactory; @@ -56,7 +55,7 @@ public class LoggerModule { @Provides @Singleton - PatternLayoutEncoder provideLayoutEncoder(LoggerContext context) { + static PatternLayoutEncoder provideLayoutEncoder(LoggerContext context) { PatternLayoutEncoder ple = new PatternLayoutEncoder(); ple.setPattern(LOG_PATTERN); ple.setContext(context); @@ -67,7 +66,7 @@ public class LoggerModule { @Provides @Singleton @Named("stdoutAppender") - Appender provideStdoutAppender(LoggerContext context, PatternLayoutEncoder encoder) { + static Appender provideStdoutAppender(LoggerContext context, PatternLayoutEncoder encoder) { ConsoleAppender appender = new ConsoleAppender<>(); appender.setContext(context); appender.setEncoder(encoder); @@ -78,7 +77,7 @@ public class LoggerModule { @Provides @Singleton @Named("fileAppender") - Appender provideFileAppender(LoggerContext context, PatternLayoutEncoder encoder, Environment environment) { + static Appender provideFileAppender(LoggerContext context, PatternLayoutEncoder encoder, Environment environment) { if (environment.getLogDir().isPresent()) { Path logDir = environment.getLogDir().get(); RollingFileAppender appender = new RollingFileAppender<>(); @@ -109,7 +108,7 @@ public class LoggerModule { @Provides @Singleton @Named("upgradeAppender") - Appender provideUpgradeAppender(LoggerContext context, PatternLayoutEncoder encoder, Environment environment) { + static Appender provideUpgradeAppender(LoggerContext context, PatternLayoutEncoder encoder, Environment environment) { if (environment.getLogDir().isPresent()) { FileAppender appender = new FileAppender<>(); appender.setFile(environment.getLogDir().get().resolve(UPGRADE_FILENAME).toString()); @@ -124,46 +123,5 @@ public class LoggerModule { } } - @Provides - @Singleton - @Named("initLogging") - Runnable provideLogbackInitializer(LoggerContext context, // - Environment environment, // - @Named("stdoutAppender") Appender stdout, // - @Named("upgradeAppender") Appender upgrade, // - @Named("fileAppender") Appender file) { - if (environment.useCustomLogbackConfig()) { - return () -> { - Logger root = context.getLogger(Logger.ROOT_LOGGER_NAME); - root.info("Using external logback configuration file."); - }; - } else { - return () -> { - context.reset(); - - // configure loggers: - for (Map.Entry loglevel : DEFAULT_LOG_LEVELS.entrySet()) { - Logger logger = context.getLogger(loglevel.getKey()); - logger.setLevel(loglevel.getValue()); - logger.setAdditive(false); - logger.addAppender(stdout); - logger.addAppender(file); - } - - // configure upgrade logger: - Logger upgrades = context.getLogger("org.cryptomator.ui.model.upgrade"); - upgrades.setLevel(Level.DEBUG); - upgrades.addAppender(stdout); - upgrades.addAppender(upgrade); - upgrades.setAdditive(false); - - // add shutdown hook - DelayingShutdownHook shutdownHook = new DelayingShutdownHook(); - shutdownHook.setContext(context); - shutdownHook.setDelay(Duration.buildByMilliseconds(SHUTDOWN_DELAY_MS)); - }; - } - } - } diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java index e94a02f53..ba28726bf 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java @@ -78,6 +78,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.stream.Stream; @@ -99,6 +100,7 @@ public class MainController implements ViewController { private final ViewControllerLoader viewControllerLoader; private final ObjectProperty activeController = new SimpleObjectProperty<>(); private final ObservableList vaults; + private final CountDownLatch shutdownLatch; private final BooleanBinding areAllVaultsLocked; private final ObjectProperty selectedVault = new SimpleObjectProperty<>(); private final ObjectExpression selectedVaultState = ObjectExpression.objectExpression(EasyBind.select(selectedVault).selectObject(Vault::stateProperty)); @@ -112,7 +114,7 @@ public class MainController implements ViewController { @Inject public MainController(@Named("mainWindow") Stage mainWindow, ExecutorService executorService, @Named("launchEventQueue") BlockingQueue launchEventQueue, ExitUtil exitUtil, Localization localization, - VaultFactory vaultFactoy, ViewControllerLoader viewControllerLoader, UpgradeStrategies upgradeStrategies, VaultList vaults, AutoUnlocker autoUnlocker) { + VaultFactory vaultFactoy, ViewControllerLoader viewControllerLoader, UpgradeStrategies upgradeStrategies, VaultList vaults, AutoUnlocker autoUnlocker, @Named("shutdownLatch") CountDownLatch shutdownLatch) { this.mainWindow = mainWindow; this.executorService = executorService; this.launchEventQueue = launchEventQueue; @@ -121,6 +123,7 @@ public class MainController implements ViewController { this.vaultFactoy = vaultFactoy; this.viewControllerLoader = viewControllerLoader; this.vaults = vaults; + this.shutdownLatch = shutdownLatch; // derived bindings: this.isShowingSettings = Bindings.equal(SettingsController.class, EasyBind.monadic(activeController).map(ViewController::getClass)); @@ -231,13 +234,13 @@ public class MainController implements ViewController { if (tryAgainButtonType.equals(btnType)) { gracefulShutdown(); } else if (forceShutdownButtonType.equals(btnType)) { - Platform.runLater(Platform::exit); + shutdownLatch.countDown(); } else { return; } }); } else { - Platform.runLater(Platform::exit); + shutdownLatch.countDown(); } } diff --git a/main/ui/src/test/java/org/cryptomator/ui/controls/SecPasswordFieldTest.java b/main/ui/src/test/java/org/cryptomator/ui/controls/SecPasswordFieldTest.java index 34fac228f..545df27be 100644 --- a/main/ui/src/test/java/org/cryptomator/ui/controls/SecPasswordFieldTest.java +++ b/main/ui/src/test/java/org/cryptomator/ui/controls/SecPasswordFieldTest.java @@ -1,5 +1,6 @@ package org.cryptomator.ui.controls; +import javafx.application.Platform; import javafx.embed.swing.JFXPanel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assumptions; @@ -22,10 +23,7 @@ class SecPasswordFieldTest { static void initJavaFx() throws InterruptedException { Assumptions.assumeFalse(GraphicsEnvironment.isHeadless()); final CountDownLatch latch = new CountDownLatch(1); - SwingUtilities.invokeLater(() -> { - new JFXPanel(); // initializes JavaFX environment - latch.countDown(); - }); + Platform.startup(latch::countDown); if (!latch.await(5L, TimeUnit.SECONDS)) { throw new ExceptionInInitializerError(); From 426f36ce04f536ba253c547634b0c357582326df Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Wed, 27 Feb 2019 17:22:48 +0100 Subject: [PATCH 36/44] fixes #753 --- main/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/pom.xml b/main/pom.xml index b8d4353b0..8b44957e0 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -27,7 +27,7 @@ 1.2.1 1.7.0 2.0.0 - 1.1.0 + 1.1.1 1.1.3 1.0.9 From 8cba58075d06b34ba1cc9c77e9d82a1244cce241 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Wed, 27 Feb 2019 17:23:08 +0100 Subject: [PATCH 37/44] fixes #840 --- .idea/runConfigurations/Cryptomator_macOS.xml | 2 +- .../org/cryptomator/ui/model/FuseVolume.java | 41 +++++++++++-------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/.idea/runConfigurations/Cryptomator_macOS.xml b/.idea/runConfigurations/Cryptomator_macOS.xml index 1e621654c..372287203 100644 --- a/.idea/runConfigurations/Cryptomator_macOS.xml +++ b/.idea/runConfigurations/Cryptomator_macOS.xml @@ -2,7 +2,7 @@