diff --git a/.travis.yml b/.travis.yml index 67ff14276..5d7a338e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,7 @@ language: java -sudo: required -dist: trusty +sudo: false jdk: -- oraclejdk8 +- oraclejdk9 cache: directories: - $HOME/.m2 @@ -12,6 +11,9 @@ env: - secure: "lV9OwUbHMrMpLUH1CY+Z4puLDdFXytudyPlG1eGRsesdpuG6KM3uQVz6uAtf6lrU8DRbMM/T7ML+PmvQ4UoPPYLdLxESLLBat2qUPOIVBOhTSlCc7I0DmGy04CSvkeMy8dPaQC0ukgNiR7zwoNzfcpGRN/U9S8tziDruuHoZSrg=" # BINTRAY_API_KEY - secure: "oWFgRTVP6lyTa7qVxlvkpm20MtVc3BtmsNXQJS6bfg2A0o/iCQMNx7OD59BaafCLGRKvCcJVESiC8FlSylVMS7CDSyYu0gg70NUiIuHp4NBM5inFWYCy/PdQsCTzr5uvNG+rMFQpMFRaCV0FrfM3tLondcVkhsHL68l93Xoexx4=" # CODACY_PROJECT_TOKEN addons: + apt: + packages: + - haveged coverity_scan: project: name: "cryptomator/cryptomator" @@ -24,6 +26,10 @@ install: - mvn -fmain/pom.xml clean package -DskipTests dependency:go-offline -Prelease script: - mvn --update-snapshots -fmain/pom.xml clean test jacoco:report verify -Pcoverage +after_success: +- jdk_switcher use oraclejdk8 +- curl -o ~/codacy-coverage-reporter-assembly-latest.jar https://oss.sonatype.org/service/local/repositories/releases/content/com/codacy/codacy-coverage-reporter/2.0.1/codacy-coverage-reporter-2.0.1-assembly.jar +- $JAVA_HOME/bin/java -cp ~/codacy-coverage-reporter-assembly-latest.jar com.codacy.CodacyCoverageReporter -l Java -r main/jacoco-report/target/site/jacoco-aggregate/jacoco.xml before_deploy: - mvn -fmain/pom.xml -Prelease clean package -DskipTests deploy: diff --git a/main/commons/pom.xml b/main/commons/pom.xml index fcef4fb7e..de0538641 100644 --- a/main/commons/pom.xml +++ b/main/commons/pom.xml @@ -38,6 +38,10 @@ com.google.dagger dagger-compiler + + com.google.auto + auto-common + com.google.dagger diff --git a/main/jacoco-report/pom.xml b/main/jacoco-report/pom.xml index e9be46b10..691a1e456 100644 --- a/main/jacoco-report/pom.xml +++ b/main/jacoco-report/pom.xml @@ -27,27 +27,6 @@ org.cryptomator launcher - - - - org.apache.logging.log4j - * - - - - - - - com.codacy - codacy-coverage-reporter - 1.0.13 - assembly - - - * - * - - @@ -66,28 +45,6 @@ - - org.codehaus.mojo - exec-maven-plugin - 1.5.0 - - - verify - - java - - - com.codacy.CodacyCoverageReporter - - -l - Java - -r - ${project.build.directory}/site/jacoco-aggregate/jacoco.xml - - - - - diff --git a/main/keychain/pom.xml b/main/keychain/pom.xml index 97721fd3b..4bb41f18c 100644 --- a/main/keychain/pom.xml +++ b/main/keychain/pom.xml @@ -38,6 +38,10 @@ com.google.dagger dagger-compiler + + com.google.auto + auto-common + diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainModule.java b/main/keychain/src/main/java/org/cryptomator/keychain/KeychainModule.java index f45492d1b..5db7bc372 100644 --- a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainModule.java +++ b/main/keychain/src/main/java/org/cryptomator/keychain/KeychainModule.java @@ -8,17 +8,27 @@ package org.cryptomator.keychain; import java.util.Optional; import java.util.Set; -import org.cryptomator.jni.JniModule; - import com.google.common.collect.Sets; - import dagger.Module; import dagger.Provides; import dagger.multibindings.ElementsIntoSet; +import org.cryptomator.jni.JniFunctions; +import org.cryptomator.jni.MacFunctions; +import org.cryptomator.jni.WinFunctions; -@Module(includes = {JniModule.class}) +@Module public class KeychainModule { + @Provides + Optional provideOptionalMacFunctions() { + return JniFunctions.macFunctions(); + } + + @Provides + Optional provideOptionalWinFunctions() { + return JniFunctions.winFunctions(); + } + @Provides @ElementsIntoSet Set provideKeychainAccessStrategies(MacSystemKeychainAccess macKeychain, WindowsProtectedKeychainAccess winKeychain) { diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/MacSystemKeychainAccess.java b/main/keychain/src/main/java/org/cryptomator/keychain/MacSystemKeychainAccess.java index 6a8122129..b3b27a4ae 100644 --- a/main/keychain/src/main/java/org/cryptomator/keychain/MacSystemKeychainAccess.java +++ b/main/keychain/src/main/java/org/cryptomator/keychain/MacSystemKeychainAccess.java @@ -15,35 +15,35 @@ import org.cryptomator.jni.MacKeychainAccess; class MacSystemKeychainAccess implements KeychainAccessStrategy { - private final MacKeychainAccess keychain; + private final Optional macFunctions; @Inject public MacSystemKeychainAccess(Optional macFunctions) { - if (macFunctions.isPresent()) { - this.keychain = macFunctions.get().keychainAccess(); - } else { - this.keychain = null; - } + this.macFunctions = macFunctions; + } + + private MacKeychainAccess keychain() { + return macFunctions.orElseThrow(IllegalStateException::new).keychainAccess(); } @Override public void storePassphrase(String key, CharSequence passphrase) { - keychain.storePassword(key, passphrase); + keychain().storePassword(key, passphrase); } @Override public char[] loadPassphrase(String key) { - return keychain.loadPassword(key); + return keychain().loadPassword(key); } @Override public boolean isSupported() { - return SystemUtils.IS_OS_MAC_OSX && keychain != null; + return SystemUtils.IS_OS_MAC_OSX && macFunctions.isPresent(); } @Override public void deletePassphrase(String key) { - keychain.deletePassword(key); + keychain().deletePassword(key); } } 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 f31536aff..57d9cdfa7 100644 --- a/main/keychain/src/main/java/org/cryptomator/keychain/WindowsProtectedKeychainAccess.java +++ b/main/keychain/src/main/java/org/cryptomator/keychain/WindowsProtectedKeychainAccess.java @@ -5,8 +5,6 @@ *******************************************************************************/ package org.cryptomator.keychain; -import static java.nio.charset.StandardCharsets.UTF_8; - import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -31,12 +29,6 @@ import java.util.UUID; import javax.inject.Inject; -import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.jni.WinDataProtection; -import org.cryptomator.jni.WinFunctions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.google.common.io.BaseEncoding; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -49,6 +41,13 @@ import com.google.gson.JsonSerializationContext; 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.jni.WinDataProtection; +import org.cryptomator.jni.WinFunctions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static java.nio.charset.StandardCharsets.UTF_8; class WindowsProtectedKeychainAccess implements KeychainAccessStrategy { @@ -57,19 +56,15 @@ class WindowsProtectedKeychainAccess implements KeychainAccessStrategy { .registerTypeHierarchyAdapter(byte[].class, new ByteArrayJsonAdapter()) // .disableHtmlEscaping().create(); - private final WinDataProtection dataProtection; + private final Optional winFunctions; private final Path keychainPath; private Map keychainEntries; @Inject public WindowsProtectedKeychainAccess(Optional winFunctions) { - if (winFunctions.isPresent()) { - this.dataProtection = winFunctions.get().dataProtection(); - } else { - this.dataProtection = null; - } + this.winFunctions = winFunctions; String keychainPathProperty = System.getProperty("cryptomator.keychainPath"); - if (dataProtection != null && keychainPathProperty == null) { + if (keychainPathProperty == null) { LOG.warn("Windows DataProtection module loaded, but no cryptomator.keychainPath property found."); } if (keychainPathProperty != null) { @@ -82,6 +77,10 @@ class WindowsProtectedKeychainAccess implements KeychainAccessStrategy { } } + private WinDataProtection dataProtection() { + return winFunctions.orElseThrow(IllegalStateException::new).dataProtection(); + } + @Override public void storePassphrase(String key, CharSequence passphrase) { loadKeychainEntriesIfNeeded(); @@ -90,7 +89,7 @@ class WindowsProtectedKeychainAccess implements KeychainAccessStrategy { buf.get(cleartext); KeychainEntry entry = new KeychainEntry(); entry.salt = generateSalt(); - entry.ciphertext = dataProtection.protect(cleartext, entry.salt); + entry.ciphertext = dataProtection().protect(cleartext, entry.salt); Arrays.fill(buf.array(), (byte) 0x00); Arrays.fill(cleartext, (byte) 0x00); keychainEntries.put(key, entry); @@ -104,7 +103,7 @@ class WindowsProtectedKeychainAccess implements KeychainAccessStrategy { if (entry == null) { return null; } - byte[] cleartext = dataProtection.unprotect(entry.ciphertext, entry.salt); + byte[] cleartext = dataProtection().unprotect(entry.ciphertext, entry.salt); if (cleartext == null) { return null; } @@ -125,7 +124,7 @@ class WindowsProtectedKeychainAccess implements KeychainAccessStrategy { @Override public boolean isSupported() { - return SystemUtils.IS_OS_WINDOWS && dataProtection != null && keychainPath != null; + return SystemUtils.IS_OS_WINDOWS && winFunctions.isPresent() && keychainPath != null; } private byte[] generateSalt() { 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 f382190a5..1bd556061 100644 --- a/main/keychain/src/test/java/org/cryptomator/keychain/KeychainModuleTest.java +++ b/main/keychain/src/test/java/org/cryptomator/keychain/KeychainModuleTest.java @@ -14,9 +14,11 @@ public class KeychainModuleTest { @Test public void testGetKeychain() { - Optional keychainAccess = DaggerTestKeychainComponent.builder().jniModule(new TestJniModule()).keychainModule(new TestKeychainModule()).build().keychainAccess(); + Optional keychainAccess = DaggerTestKeychainComponent.builder().keychainModule(new TestKeychainModule()).build().keychainAccess(); Assert.assertTrue(keychainAccess.isPresent()); Assert.assertTrue(keychainAccess.get() instanceof MapKeychainAccess); + keychainAccess.get().storePassphrase("test", "asd"); + Assert.assertArrayEquals("asd".toCharArray(), keychainAccess.get().loadPassphrase("test")); } } diff --git a/main/keychain/src/test/java/org/cryptomator/keychain/TestJniModule.java b/main/keychain/src/test/java/org/cryptomator/keychain/TestJniModule.java deleted file mode 100644 index 6c54e2b4f..000000000 --- a/main/keychain/src/test/java/org/cryptomator/keychain/TestJniModule.java +++ /dev/null @@ -1,28 +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.keychain; - -import java.util.Optional; - -import org.cryptomator.jni.JniModule; -import org.cryptomator.jni.MacFunctions; -import org.cryptomator.jni.WinFunctions; - -import dagger.Lazy; - -public class TestJniModule extends JniModule { - - @Override - public Optional winFunctions(Lazy winFunction) { - return Optional.empty(); - } - - @Override - public Optional macFunctions(Lazy winFunction) { - return Optional.empty(); - } - -} diff --git a/main/launcher/pom.xml b/main/launcher/pom.xml index 3c97807ef..65b6c492b 100644 --- a/main/launcher/pom.xml +++ b/main/launcher/pom.xml @@ -38,6 +38,10 @@ com.google.dagger dagger-compiler + + com.google.auto + auto-common + 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 3a0ac97c9..854876056 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java @@ -6,6 +6,7 @@ *******************************************************************************/ package org.cryptomator.launcher; +import java.awt.Desktop; import java.io.File; import java.nio.file.FileSystem; import java.nio.file.FileSystems; @@ -13,7 +14,6 @@ import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.util.concurrent.BlockingQueue; -import org.cryptomator.ui.util.EawtApplicationWrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,10 +24,8 @@ class FileOpenRequestHandler { public FileOpenRequestHandler(BlockingQueue fileOpenRequests) { this.fileOpenRequests = fileOpenRequests; - EawtApplicationWrapper.getApplication().ifPresent(app -> { - app.setOpenFileHandler(files -> { - files.stream().map(File::toPath).forEach(fileOpenRequests::add); - }); + Desktop.getDesktop().setOpenFileHandler(e -> { + e.getFiles().stream().map(File::toPath).forEach(fileOpenRequests::add); }); } diff --git a/main/pom.xml b/main/pom.xml index 6c939e273..ca0fb5ae4 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -27,7 +27,7 @@ 1.1.7 1.4.5 1.0.3 - 1.0.2 + 1.1.0-SNAPSHOT 2.5 3.6 @@ -36,7 +36,7 @@ 1.0.3 23.5-jre - 2.11 + 2.14 2.8.2 1.7.25 @@ -169,6 +169,14 @@ dagger-compiler ${dagger.version} true + provided + + + com.google.auto + auto-common + 0.9 + true + provided com.google.code.gson diff --git a/main/ui/pom.xml b/main/ui/pom.xml index c5a0980c9..a9263a3b4 100644 --- a/main/ui/pom.xml +++ b/main/ui/pom.xml @@ -76,6 +76,10 @@ com.google.dagger dagger-compiler + + com.google.auto + auto-common + 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 60f6a76e7..4bcc9b877 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/UiModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/UiModule.java @@ -16,22 +16,20 @@ 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.settings.Settings; import org.cryptomator.common.settings.SettingsProvider; import org.cryptomator.frontend.webdav.WebDavServer; -import org.cryptomator.jni.JniModule; import org.cryptomator.keychain.KeychainModule; import org.cryptomator.ui.controllers.ViewControllerModule; import org.cryptomator.ui.model.VaultComponent; import org.fxmisc.easybind.EasyBind; -import dagger.Module; -import dagger.Provides; -import javafx.beans.binding.Binding; - -@Module(includes = {ViewControllerModule.class, CommonsModule.class, KeychainModule.class, JniModule.class}, subcomponents = {VaultComponent.class}) +@Module(includes = {ViewControllerModule.class, CommonsModule.class, KeychainModule.class}, subcomponents = {VaultComponent.class}) public class UiModule { @Provides 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 682cd0afb..5eabc09e8 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,8 +9,7 @@ ******************************************************************************/ package org.cryptomator.ui.controllers; -import static org.cryptomator.ui.util.DialogBuilderUtil.buildErrorDialog; - +import java.awt.Desktop; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -28,25 +27,6 @@ import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; -import org.apache.commons.lang3.SystemUtils; -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.AutoUnlocker; -import org.cryptomator.ui.model.UpgradeStrategies; -import org.cryptomator.ui.model.UpgradeStrategy; -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.EawtApplicationWrapper; -import org.fxmisc.easybind.EasyBind; -import org.fxmisc.easybind.Subscription; -import org.fxmisc.easybind.monadic.MonadicBinding; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import javafx.application.Application; import javafx.application.Platform; import javafx.beans.binding.Binding; @@ -81,6 +61,25 @@ import javafx.scene.layout.Pane; import javafx.scene.text.Font; import javafx.stage.FileChooser; import javafx.stage.Stage; +import org.apache.commons.lang3.SystemUtils; +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.AutoUnlocker; +import org.cryptomator.ui.model.UpgradeStrategies; +import org.cryptomator.ui.model.UpgradeStrategy; +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.fxmisc.easybind.EasyBind; +import org.fxmisc.easybind.Subscription; +import org.fxmisc.easybind.monadic.MonadicBinding; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.cryptomator.ui.util.DialogBuilderUtil.buildErrorDialog; @Singleton public class MainController implements ViewController { @@ -129,10 +128,8 @@ public class MainController implements ViewController { EasyBind.subscribe(areAllVaultsLocked, Platform::setImplicitExit); autoUnlocker.unlockAllSilently(); - EawtApplicationWrapper.getApplication().ifPresent(app -> { - app.setPreferencesHandler(() -> { - Platform.runLater(this::toggleShowSettings); - }); + Desktop.getDesktop().setPreferencesHandler(e -> { + Platform.runLater(this::toggleShowSettings); }); } 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 f2c9718fc..5ce2685fa 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 @@ -38,6 +38,11 @@ public class VaultList extends TransformationList { return index; } + @Override + public int getViewIndex(int index) { + return index; + } + @Override public Vault get(int index) { VaultSettings s = source.get(index); diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/EawtApplicationWrapper.java b/main/ui/src/main/java/org/cryptomator/ui/util/EawtApplicationWrapper.java deleted file mode 100644 index 3c524e418..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/util/EawtApplicationWrapper.java +++ /dev/null @@ -1,127 +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.ui.util; - -import java.io.File; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.List; -import java.util.Optional; -import java.util.function.Consumer; -import java.util.function.Function; - -import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.common.SupplierThrowingException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Reflection-based wrapper for com.apple.eawt.Application. - */ -public class EawtApplicationWrapper { - - private static final Logger LOG = LoggerFactory.getLogger(EawtApplicationWrapper.class); - - private final Class applicationClass; - private final Object application; - - private EawtApplicationWrapper() throws ReflectiveOperationException { - this.applicationClass = Class.forName("com.apple.eawt.Application"); - this.application = applicationClass.getMethod("getApplication").invoke(null); - } - - /** - * @return A wrapper for com.apple.ewat.Application if the current OS is macOS and the class is available in this JVM. - */ - public static Optional getApplication() { - if (!SystemUtils.IS_OS_MAC_OSX) { - return Optional.empty(); - } - try { - return Optional.of(new EawtApplicationWrapper()); - } catch (ReflectiveOperationException e) { - return Optional.empty(); - } - } - - private void setOpenFileHandler(InvocationHandler handler) throws ReflectiveOperationException { - Class handlerClass = Class.forName("com.apple.eawt.OpenFilesHandler"); - Method setter = applicationClass.getMethod("setOpenFileHandler", handlerClass); - Object proxy = Proxy.newProxyInstance(applicationClass.getClassLoader(), new Class[] {handlerClass}, handler); - setter.invoke(application, proxy); - } - - public void setOpenFileHandler(Consumer> handler) { - try { - Class openFilesEventClass = Class.forName("com.apple.eawt.AppEvent$OpenFilesEvent"); - Method getFiles = openFilesEventClass.getMethod("getFiles"); - setOpenFileHandler(methodSpecificInvocationHandler("openFiles", args -> { - Object openFilesEvent = args[0]; - assert openFilesEventClass.isInstance(openFilesEvent); - @SuppressWarnings("unchecked") - List files = (List) uncheckedReflectiveOperation(() -> getFiles.invoke(openFilesEvent)); - handler.accept(files); - return null; - })); - } catch (ReflectiveOperationException e) { - LOG.error("Exception setting openFileHandler.", e); - } - } - - private void setPreferencesHandler(InvocationHandler handler) throws ReflectiveOperationException { - Class handlerClass = Class.forName("com.apple.eawt.PreferencesHandler"); - Method setter = applicationClass.getMethod("setPreferencesHandler", handlerClass); - Object proxy = Proxy.newProxyInstance(applicationClass.getClassLoader(), new Class[] {handlerClass}, handler); - setter.invoke(application, proxy); - } - - public void setPreferencesHandler(Runnable handler) { - try { - setPreferencesHandler(methodSpecificInvocationHandler("handlePreferences", args -> { - handler.run(); - return null; - })); - } catch (ReflectiveOperationException e) { - LOG.error("Exception setting preferencesHandler.", e); - } - } - - private static InvocationHandler methodSpecificInvocationHandler(String methodName, Function handler) { - return new InvocationHandler() { - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if (method.getName().equals(methodName)) { - return handler.apply(args); - } else { - throw new UnsupportedOperationException("Unexpected invocation " + method.getName() + ", expected " + methodName); - } - } - }; - } - - /** - * Wraps {@link ReflectiveOperationException}s as {@link UncheckedReflectiveOperationException}. - * - * @param operation Invokation throwing an ReflectiveOperationException - * @return Result returned by operation - * @throws UncheckedReflectiveOperationException in case operation throws an ReflectiveOperationException. - */ - private static T uncheckedReflectiveOperation(SupplierThrowingException operation) throws UncheckedReflectiveOperationException { - try { - return operation.get(); - } catch (ReflectiveOperationException e) { - throw new UncheckedReflectiveOperationException(e); - } - } - - private static class UncheckedReflectiveOperationException extends RuntimeException { - public UncheckedReflectiveOperationException(ReflectiveOperationException cause) { - super(cause); - } - } - -}