diff --git a/main/commons/src/main/java/org/cryptomator/common/mountpoint/AvailableDriveLetterChooser.java b/main/commons/src/main/java/org/cryptomator/common/mountpoint/AvailableDriveLetterChooser.java index 0d94e8136..86e64427d 100644 --- a/main/commons/src/main/java/org/cryptomator/common/mountpoint/AvailableDriveLetterChooser.java +++ b/main/commons/src/main/java/org/cryptomator/common/mountpoint/AvailableDriveLetterChooser.java @@ -8,6 +8,8 @@ import java.util.Optional; public class AvailableDriveLetterChooser implements MountPointChooser { + private static final int POSITION = 200; + private final WindowsDriveLetters windowsDriveLetters; public AvailableDriveLetterChooser(WindowsDriveLetters windowsDriveLetters) { @@ -23,4 +25,9 @@ public class AvailableDriveLetterChooser implements MountPointChooser { public Optional chooseMountPoint() { return this.windowsDriveLetters.getAvailableDriveLetterPath(); } + + @Override + public int getPosition() { + return POSITION; + } } diff --git a/main/commons/src/main/java/org/cryptomator/common/mountpoint/CustomDriveLetterChooser.java b/main/commons/src/main/java/org/cryptomator/common/mountpoint/CustomDriveLetterChooser.java index f01b04289..fd3d2ae68 100644 --- a/main/commons/src/main/java/org/cryptomator/common/mountpoint/CustomDriveLetterChooser.java +++ b/main/commons/src/main/java/org/cryptomator/common/mountpoint/CustomDriveLetterChooser.java @@ -9,6 +9,8 @@ import java.util.Optional; public class CustomDriveLetterChooser implements MountPointChooser { + public static final int POSITION = 100; + private final Vault vault; public CustomDriveLetterChooser(Vault vault) { @@ -24,4 +26,9 @@ public class CustomDriveLetterChooser implements MountPointChooser { public Optional chooseMountPoint() { return this.vault.getVaultSettings().getWinDriveLetter().map(letter -> letter.charAt(0) + ":\\").map(Paths::get); } + + @Override + public int getPosition() { + return POSITION; + } } diff --git a/main/commons/src/main/java/org/cryptomator/common/mountpoint/CustomMountPointChooser.java b/main/commons/src/main/java/org/cryptomator/common/mountpoint/CustomMountPointChooser.java index 55ad471ad..0a07f7ec3 100644 --- a/main/commons/src/main/java/org/cryptomator/common/mountpoint/CustomMountPointChooser.java +++ b/main/commons/src/main/java/org/cryptomator/common/mountpoint/CustomMountPointChooser.java @@ -18,6 +18,8 @@ import java.util.Optional; public class CustomMountPointChooser implements MountPointChooser { + public static final int POSITION = 0; + private static final Logger LOG = LoggerFactory.getLogger(CustomMountPointChooser.class); private final Vault vault; @@ -26,6 +28,11 @@ public class CustomMountPointChooser implements MountPointChooser { this.vault = vault; } + @Override + public boolean isApplicable() { + return true; + } + @Override public Optional chooseMountPoint() { //VaultSettings#getCustomMountPath already checks whether the saved custom mountpoint should be used @@ -73,4 +80,9 @@ public class CustomMountPointChooser implements MountPointChooser { LOG.debug("Successfully checked custom mount point: {}", mountPoint); return false; } + + @Override + public int getPosition() { + return POSITION; + } } diff --git a/main/commons/src/main/java/org/cryptomator/common/mountpoint/MountPointChooser.java b/main/commons/src/main/java/org/cryptomator/common/mountpoint/MountPointChooser.java index fb5a6eaa5..79326aeb3 100644 --- a/main/commons/src/main/java/org/cryptomator/common/mountpoint/MountPointChooser.java +++ b/main/commons/src/main/java/org/cryptomator/common/mountpoint/MountPointChooser.java @@ -1,65 +1,63 @@ package org.cryptomator.common.mountpoint; -import dagger.MapKey; import org.cryptomator.common.vaults.Volume; import java.nio.file.Path; import java.util.Optional; -import java.util.Set; +import java.util.SortedSet; /** * Base interface for the Mountpoint-Choosing-Operation that results in the choice and * preparation of a Mountpoint or an exception otherwise.
*

All MountPointChoosers (MPCs) need to implement this class and must be added to - * the pool of possible MPCs by {@link MountPointChooserModule MountPointChooserModule.} - * The MountPointChooserModule requires all {@link dagger.Provides Providermethods} to - * be annotated with the {@link PhaseKey @PhaseKey-Annotation} and a unique Phase to - * allow the Module to sort them according to the phases' {@link Phase#getTiming()} timing.} - * The timing must be defined by the developer to reflect a useful execution order. + * the pool of possible MPCs by the {@link MountPointChooserModule MountPointChooserModule.} + * The MountPointChooserModule will sort them according to their {@link #getPosition() position.} + * The position must be defined by the developer to reflect a useful execution order; + * the order of execution of MPCs with equal position is undefined. * - *

Phase-Uniqueness: Phases must be unique, meaning that they must not be used - * to annotate more than one Providermethod. Define a new phase for each additional - * MPC that is added to the Module. Timings can be reused; the order of Phases with equal - * timing among themselves is undefined. + *

MPCs are executed by a {@link Volume} in ascedning order of their position to find + * and prepare a suitable Mountpoint for the Volume. The Volume has access to a + * {@link SortedSet} of MPCs in this specific order, that is provided by the Module. + * The Set only contains Choosers that were deemed {@link #isApplicable() applicable} + * by the Module. * - *

MPCs are executed by a {@link Volume} in the order of their phase's timing to find - * and prepare a suitable Mountpoint for the Volume. The Volume only has access to a {@link Set} - * of MPCs in this specific order, that is provided by the Module; the according Phases and exact - * timings are inaccessible to the Volume. The Set only contains Choosers that were deemed - * {@link #isApplicable() applicable} by the Module. + *

At execution of a MPC {@link #chooseMountPoint()} is called to choose a Mountpoint + * according to the MPC's strategy. The strategy can involve reading configs, + * searching the filesystem, processing user-input or similar operations. + * If {@code #chooseMountPoint()} returns a non-null path (everthing but + * {@linkplain Optional#empty()}) the MPC's {@link #prepare(Path)}-Method is called and the + * MountPoint is verfied and/or prepared. In this case no other MPC's will be called for + * this Volume, even if {@code #prepare(Path)} fails. * - *

At execution of a MPC {@link #chooseMountPoint()} and then {@link #prepare(Path)} are called - * by the Volume. If {@code #chooseMountPoint()} yields no result, the next MPC is executed - * without first calling the {@code #prepare(Path)}-Method of the current MPC. + *

If {@code #chooseMountPoint()} yields no result, the next MPC is executed + * without first calling the {@code #prepare(Path)}-Method of the current MPC. * This is repeated until
*

    *
  • either a Mountpoint is returned by {@code #chooseMountPoint()} - * and {@code #prepare(Path)} succeeds or fails
  • + * and {@code #prepare(Path)} succeeds or fails, ending the entire operation *
  • or no MPC remains and an {@link InvalidMountPointException} is thrown.
  • *
- * If the {@code #prepare(Path)}-Method of a MPC fails the entire Mountpoint-Choosing-Operation + * If the {@code #prepare(Path)}-Method of a MPC fails, the entire Mountpoint-Choosing-Operation * is aborted and the method should do all necessary cleanup before throwing the exception. * If the preparation succeeds {@link #cleanup(Path)} can be used after unmount to do any * remaining cleanup. */ -public interface MountPointChooser { +public interface MountPointChooser extends Comparable { /** * Called by the {@link MountPointChooserModule} to determine whether this MountPointChooser is * applicable for the given Systemconfiguration. * - *

The result of this method defaults to true. Developers should override this method to + *

Developers should override this method to * check for Systemconfigurations that are unsuitable for this MPC. * * @return a boolean flag; true if applicable, else false. * @see #chooseMountPoint() */ - default boolean isApplicable() { - return true; //Usually most of the choosers should be applicable - } + boolean isApplicable(); /** - * Called by a {@link Volume} to do choose a Mountpoint according to the + * Called by a {@link Volume} to choose a Mountpoint according to the * MountPointChoosers strategy. * *

This method is only called for MPCs that were deemed {@link #isApplicable() applicable} @@ -128,43 +126,32 @@ public interface MountPointChooser { } /** - * The phases of the existing {@link MountPointChooser MountPointChoosers.} - *

The {@code Phases} of the MPCs are attached to them in the - * {@link MountPointChooserModule} by annotating them with the - * {@link PhaseKey @PhaseKey-Annotation.} - *

Each MPC must have a unique Phase that allows the Module to sort - * the MPCs according to their phases' {@link Phase#getTiming()} timing.} - * The timing must be defined by the developer to reflect a useful execution order. + * Called by the {@link MountPointChooserModule} to sort the available MPCs + * and determine their execution order. + * The position must be defined by the developer to reflect a useful execution order. + * MPCs with lower positions will be placed at lower indices in the resulting + * {@link SortedSet} and will be executed with higher probability. + * The order of execution of MPCs with equal position is undefined. * - *

Phase-Uniqueness: Phases must be unique, meaning that they must not be used - * to annotate more than one Providermethod. Define a new phase for each additional - * MPC that is added to the Module. Timings can be reused; the order of Phases with equal - * timings among themselves is undefined. + * @return the position of this MPC. */ - enum Phase { + int getPosition(); - CUSTOM_MOUNTPOINT(0), - - CUSTOM_DRIVELETTER(1), - - AVAILABLE_DRIVELETTER(2), - - TEMPORARY_MOUNTPOINT(3); - - private final int timing; - - Phase(int timing) { - this.timing = timing; - } - - public int getTiming() { - return timing; - } - } - - @MapKey - @interface PhaseKey { - - Phase value(); + /** + * Called by the {@link MountPointChooserModule} to determine the execution order + * of the registered MPCs. Implementations usually should not override this + * method. This default implementation sorts the MPCs in ascending order + * of their {@link #getPosition() position.}
+ *
+ * Original description: + *

{@inheritDoc} + * + * @implNote This default implementation sorts the MPCs in ascending order + * of their {@link #getPosition() position.} + */ + @Override + default int compareTo(MountPointChooser other) { + //Sort by position (ascending order) + return Integer.compare(this.getPosition(), other.getPosition()); } } diff --git a/main/commons/src/main/java/org/cryptomator/common/mountpoint/MountPointChooserModule.java b/main/commons/src/main/java/org/cryptomator/common/mountpoint/MountPointChooserModule.java index 6f1fd0806..9a2332c94 100644 --- a/main/commons/src/main/java/org/cryptomator/common/mountpoint/MountPointChooserModule.java +++ b/main/commons/src/main/java/org/cryptomator/common/mountpoint/MountPointChooserModule.java @@ -3,16 +3,13 @@ package org.cryptomator.common.mountpoint; import com.google.common.collect.ImmutableSet; import dagger.Module; import dagger.Provides; -import dagger.multibindings.IntoMap; +import dagger.multibindings.IntoSet; import org.cryptomator.common.Environment; -import org.cryptomator.common.mountpoint.MountPointChooser.Phase; import org.cryptomator.common.vaults.PerVault; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.WindowsDriveLetters; import javax.inject.Named; -import java.util.Comparator; -import java.util.Map; import java.util.Set; /** @@ -25,32 +22,28 @@ import java.util.Set; public abstract class MountPointChooserModule { @Provides - @IntoMap - @MountPointChooser.PhaseKey(Phase.CUSTOM_MOUNTPOINT) + @IntoSet @PerVault public static MountPointChooser provideCustomMountPointChooser(Vault vault) { return new CustomMountPointChooser(vault); } @Provides - @IntoMap - @MountPointChooser.PhaseKey(Phase.CUSTOM_DRIVELETTER) + @IntoSet @PerVault public static MountPointChooser provideCustomDriveLetterChooser(Vault vault) { return new CustomDriveLetterChooser(vault); } @Provides - @IntoMap - @MountPointChooser.PhaseKey(Phase.AVAILABLE_DRIVELETTER) + @IntoSet @PerVault public static MountPointChooser provideAvailableDriveLetterChooser(WindowsDriveLetters windowsDriveLetters) { return new AvailableDriveLetterChooser(windowsDriveLetters); } @Provides - @IntoMap - @MountPointChooser.PhaseKey(Phase.TEMPORARY_MOUNTPOINT) + @IntoSet @PerVault public static MountPointChooser provideTemporaryMountPointChooser(Vault vault, Environment environment) { return new TemporaryMountPointChooser(vault, environment); @@ -59,12 +52,8 @@ public abstract class MountPointChooserModule { @Provides @PerVault @Named("orderedValidMountPointChoosers") - public static Set provideOrderedValidMountPointChoosers(Map chooserMap) { + public static Set provideOrderedValidMountPointChoosers(Set choosers) { //Sorted Set - return chooserMap.entrySet().stream() - .sorted(Comparator.comparingInt(value -> value.getKey().getTiming())) - .map(Map.Entry::getValue) - .filter(MountPointChooser::isApplicable) - .collect(ImmutableSet.toImmutableSet()); + return choosers.stream().sorted().filter(MountPointChooser::isApplicable).collect(ImmutableSet.toImmutableSet()); } } diff --git a/main/commons/src/main/java/org/cryptomator/common/mountpoint/TemporaryMountPointChooser.java b/main/commons/src/main/java/org/cryptomator/common/mountpoint/TemporaryMountPointChooser.java index 5924bf2ef..786f1961f 100644 --- a/main/commons/src/main/java/org/cryptomator/common/mountpoint/TemporaryMountPointChooser.java +++ b/main/commons/src/main/java/org/cryptomator/common/mountpoint/TemporaryMountPointChooser.java @@ -15,6 +15,8 @@ import java.util.Optional; public class TemporaryMountPointChooser implements MountPointChooser { + public static final int POSITION = 300; + private static final Logger LOG = LoggerFactory.getLogger(TemporaryMountPointChooser.class); private static final int MAX_TMPMOUNTPOINT_CREATION_RETRIES = 10; @@ -85,4 +87,9 @@ public class TemporaryMountPointChooser implements MountPointChooser { LOG.warn("Could not delete mount point: {}", e.getMessage()); } } + + @Override + public int getPosition() { + return POSITION; + } }