Skip to content

Commit d575f5d

Browse files
Merge pull request #7 from cryptomator/feature/service-loading
Integrated Service Loading
2 parents a7cdfc8 + 6263463 commit d575f5d

File tree

8 files changed

+165
-0
lines changed

8 files changed

+165
-0
lines changed

src/main/java/module-info.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
1+
import org.cryptomator.integrations.autostart.AutoStartProvider;
2+
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
3+
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
4+
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
5+
16
module org.cryptomator.integrations.api {
27
exports org.cryptomator.integrations.autostart;
38
exports org.cryptomator.integrations.keychain;
49
exports org.cryptomator.integrations.tray;
510
exports org.cryptomator.integrations.uiappearance;
11+
12+
uses AutoStartProvider;
13+
uses KeychainAccessProvider;
14+
uses TrayIntegrationProvider;
15+
uses UiAppearanceProvider;
616
}

src/main/java/org/cryptomator/integrations/autostart/AutoStartProvider.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
package org.cryptomator.integrations.autostart;
22

3+
import org.cryptomator.integrations.common.IntegrationsLoader;
4+
5+
import java.util.Optional;
6+
37
public interface AutoStartProvider {
48

9+
static Optional<AutoStartProvider> get() {
10+
return IntegrationsLoader.load(AutoStartProvider.class);
11+
}
12+
513
void enable() throws ToggleAutoStartFailedException;
614

715
void disable() throws ToggleAutoStartFailedException;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package org.cryptomator.integrations.common;
2+
3+
import java.util.Arrays;
4+
import java.util.Comparator;
5+
import java.util.Optional;
6+
import java.util.ServiceLoader;
7+
import java.util.stream.Stream;
8+
9+
public class IntegrationsLoader {
10+
11+
/**
12+
* Loads the best suited service, i.e. the one with the highest priority that is supported.
13+
* <p>
14+
* If two services are available with the same priority, it is unspecified which one will be returned.
15+
*
16+
* @param clazz Service class
17+
* @param <T> Type of the service
18+
* @return Highest priority service or empty if no supported service was found
19+
*/
20+
public static <T> Optional<T> load(Class<T> clazz) {
21+
return loadAll(clazz).findFirst();
22+
}
23+
24+
/**
25+
* Loads all suited services ordered by priority in descending order.
26+
*
27+
* @param clazz Service class
28+
* @param <T> Type of the service
29+
* @return An ordered stream of all suited service candidates
30+
*/
31+
public static <T> Stream<T> loadAll(Class<T> clazz) {
32+
return ServiceLoader.load(clazz)
33+
.stream()
34+
.filter(IntegrationsLoader::isSupportedOperatingSystem)
35+
.sorted(Comparator.comparingInt(IntegrationsLoader::getPriority).reversed())
36+
.map(ServiceLoader.Provider::get);
37+
}
38+
39+
private static int getPriority(ServiceLoader.Provider<?> provider) {
40+
var prio = provider.type().getAnnotation(Priority.class);
41+
return prio == null ? Priority.DEFAULT : prio.value();
42+
}
43+
44+
private static boolean isSupportedOperatingSystem(ServiceLoader.Provider<?> provider) {
45+
var annotations = provider.type().getAnnotationsByType(OperatingSystem.class);
46+
return annotations.length == 0 || Arrays.stream(annotations).anyMatch(OperatingSystem.Value::isCurrent);
47+
}
48+
49+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package org.cryptomator.integrations.common;
2+
3+
import java.lang.annotation.Documented;
4+
import java.lang.annotation.ElementType;
5+
import java.lang.annotation.Repeatable;
6+
import java.lang.annotation.Retention;
7+
import java.lang.annotation.RetentionPolicy;
8+
import java.lang.annotation.Target;
9+
10+
/**
11+
* Restricts the annotated integration provider to one or more operating system(s).
12+
*/
13+
@Documented
14+
@Retention(RetentionPolicy.RUNTIME)
15+
@Target({ElementType.TYPE})
16+
@Repeatable(OperatingSystem.OperatingSystems.class)
17+
public @interface OperatingSystem {
18+
Value value() default Value.UNKNOWN;
19+
20+
@Documented
21+
@Retention(RetentionPolicy.RUNTIME)
22+
@Target({ElementType.TYPE})
23+
@interface OperatingSystems {
24+
OperatingSystem[] value();
25+
}
26+
27+
enum Value {
28+
LINUX,
29+
MAC,
30+
WINDOWS,
31+
UNKNOWN;
32+
33+
private static final String OS_NAME = System.getProperty("os.name", "").toLowerCase();
34+
35+
public static Value current() {
36+
if (OS_NAME.contains("linux")) {
37+
return LINUX;
38+
} else if (OS_NAME.contains("mac")) {
39+
return MAC;
40+
} else if (OS_NAME.contains("windows")) {
41+
return WINDOWS;
42+
} else {
43+
return UNKNOWN;
44+
}
45+
}
46+
47+
public static boolean isCurrent(OperatingSystem os) {
48+
return current().equals(os.value());
49+
}
50+
}
51+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package org.cryptomator.integrations.common;
2+
3+
import java.lang.annotation.Documented;
4+
import java.lang.annotation.ElementType;
5+
import java.lang.annotation.Repeatable;
6+
import java.lang.annotation.Retention;
7+
import java.lang.annotation.RetentionPolicy;
8+
import java.lang.annotation.Target;
9+
10+
/**
11+
* Integration Priority.
12+
* <p>
13+
* If multiple implementations for an integration can be provided, the provider with the highest priority will be used.
14+
*/
15+
@Documented
16+
@Retention(RetentionPolicy.RUNTIME)
17+
@Target({ElementType.TYPE})
18+
public @interface Priority {
19+
int DEFAULT = 0;
20+
int FALLBACK = Integer.MIN_VALUE;
21+
22+
int value() default DEFAULT;
23+
}

src/main/java/org/cryptomator/integrations/keychain/KeychainAccessProvider.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
package org.cryptomator.integrations.keychain;
22

3+
import org.cryptomator.integrations.common.IntegrationsLoader;
4+
5+
import java.util.stream.Stream;
6+
37
/**
48
* This is the interface used by Cryptomator to store passwords securely in external keychains, such as system keychains or password managers.
59
*/
610
public interface KeychainAccessProvider {
711

12+
static Stream<KeychainAccessProvider> get() {
13+
return IntegrationsLoader.loadAll(KeychainAccessProvider.class).filter(KeychainAccessProvider::isSupported);
14+
}
15+
816
/**
917
* A name to display in UI elements. If required, this should be localized.
1018
*

src/main/java/org/cryptomator/integrations/tray/TrayIntegrationProvider.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
package org.cryptomator.integrations.tray;
22

3+
import org.cryptomator.integrations.common.IntegrationsLoader;
4+
5+
import java.util.Optional;
6+
37
public interface TrayIntegrationProvider {
48

9+
static Optional<TrayIntegrationProvider> get() {
10+
return IntegrationsLoader.load(TrayIntegrationProvider.class);
11+
}
12+
513
/**
614
* Performs tasks required when the application is no longer showing any window and only accessible via
715
* system tray (or comparable facilities).

src/main/java/org/cryptomator/integrations/uiappearance/UiAppearanceProvider.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
package org.cryptomator.integrations.uiappearance;
22

3+
import org.cryptomator.integrations.common.IntegrationsLoader;
4+
5+
import java.util.Optional;
6+
37
/**
48
* This is the interface used by Cryptomator to get os specific UI appearances and themes.
59
*/
610
public interface UiAppearanceProvider {
711

12+
static Optional<UiAppearanceProvider> get() {
13+
return IntegrationsLoader.load(UiAppearanceProvider.class);
14+
}
15+
816
/**
917
* Gets the best-matching theme for the OS's current L&amp;F. This might be an approximation, as the OS might support more variations than we do.
1018
*

0 commit comments

Comments
 (0)