diff --git a/src/main/java/dev/coph/flightscore/backend/competition/CompetitionProvider.java b/src/main/java/dev/coph/flightscore/backend/competition/CompetitionProvider.java index c85bf0d..4123b1c 100644 --- a/src/main/java/dev/coph/flightscore/backend/competition/CompetitionProvider.java +++ b/src/main/java/dev/coph/flightscore/backend/competition/CompetitionProvider.java @@ -6,11 +6,6 @@ import dev.coph.simplesql.query.Query; public class CompetitionProvider implements Provider { - @Override - public int priority() { - return 0; - } - @Override public String key() { return ""; diff --git a/src/main/java/dev/coph/flightscore/backend/provider/Provider.java b/src/main/java/dev/coph/flightscore/backend/provider/Provider.java index 502158a..ee4a425 100644 --- a/src/main/java/dev/coph/flightscore/backend/provider/Provider.java +++ b/src/main/java/dev/coph/flightscore/backend/provider/Provider.java @@ -2,8 +2,12 @@ package dev.coph.flightscore.backend.provider; import dev.coph.simplesql.query.Query; +import java.util.Set; + public interface Provider { - int priority(); + default Set dependsOn() { + return Set.of(); + } String key(); diff --git a/src/main/java/dev/coph/flightscore/backend/provider/ProviderManager.java b/src/main/java/dev/coph/flightscore/backend/provider/ProviderManager.java index f3befc3..8a22dae 100644 --- a/src/main/java/dev/coph/flightscore/backend/provider/ProviderManager.java +++ b/src/main/java/dev/coph/flightscore/backend/provider/ProviderManager.java @@ -3,7 +3,13 @@ package dev.coph.flightscore.backend.provider; import dev.coph.flightscore.backend.Backend; import dev.coph.simplesql.query.Query; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; public class ProviderManager { private final Backend backend; @@ -15,13 +21,13 @@ public class ProviderManager { public void createAllDatabaseTables() { Query query = new Query(backend.databaseAdapter()); - providers.values().stream().sorted((a, b) -> Integer.compare(b.priority(), a.priority())).forEach(provider -> provider.createDatabaseTables(query)); + resolveOrder().forEach(provider -> provider.createDatabaseTables(query)); query.execute(); } public void enableAllProviders() { Query query = new Query(backend.databaseAdapter()); - providers.values().stream().sorted((a, b) -> Integer.compare(b.priority(), a.priority())).forEach(provider -> provider.onEnable(query)); + resolveOrder().forEach(provider -> provider.onEnable(query)); query.execute(); } @@ -33,4 +39,49 @@ public class ProviderManager { return (T) providers.get(key); } + private List resolveOrder() { + HashMap inDegree = new HashMap<>(); + HashMap> dependents = new HashMap<>(); + for (Provider provider : providers.values()) { + inDegree.putIfAbsent(provider.key(), 0); + dependents.putIfAbsent(provider.key(), new HashSet<>()); + } + for (Provider provider : providers.values()) { + for (String dependency : provider.dependsOn()) { + if (dependency.equals(provider.key())) { + throw new IllegalStateException("Provider '" + provider.key() + "' depends on itself."); + } + if (!providers.containsKey(dependency)) { + throw new IllegalStateException("Provider '" + provider.key() + "' depends on unknown provider '" + dependency + "'."); + } + if (dependents.get(dependency).add(provider.key())) { + inDegree.merge(provider.key(), 1, Integer::sum); + } + } + } + + Deque ready = new ArrayDeque<>(); + inDegree.forEach((key, degree) -> { + if (degree == 0) ready.add(key); + }); + + List ordered = new ArrayList<>(providers.size()); + while (!ready.isEmpty()) { + String key = ready.poll(); + ordered.add(providers.get(key)); + for (String dependent : dependents.get(key)) { + if (inDegree.merge(dependent, -1, Integer::sum) == 0) { + ready.add(dependent); + } + } + } + + if (ordered.size() != providers.size()) { + Set remaining = new HashSet<>(providers.keySet()); + ordered.forEach(provider -> remaining.remove(provider.key())); + throw new IllegalStateException("Circular dependency detected among providers: " + remaining); + } + return ordered; + } + } diff --git a/src/main/java/dev/coph/flightscore/backend/user/UserProvider.java b/src/main/java/dev/coph/flightscore/backend/user/UserProvider.java index caf5105..052cdd9 100644 --- a/src/main/java/dev/coph/flightscore/backend/user/UserProvider.java +++ b/src/main/java/dev/coph/flightscore/backend/user/UserProvider.java @@ -29,6 +29,7 @@ import java.time.Duration; import java.time.Instant; import java.util.List; import java.util.Locale; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -45,8 +46,8 @@ public class UserProvider implements Provider { } @Override - public int priority() { - return 50; + public Set dependsOn() { + return Set.of("role-provider"); } @Override diff --git a/src/main/java/dev/coph/flightscore/backend/user/permission/PermissionProvider.java b/src/main/java/dev/coph/flightscore/backend/user/permission/PermissionProvider.java index 46f0db0..d21ea60 100644 --- a/src/main/java/dev/coph/flightscore/backend/user/permission/PermissionProvider.java +++ b/src/main/java/dev/coph/flightscore/backend/user/permission/PermissionProvider.java @@ -20,11 +20,6 @@ public class PermissionProvider implements Provider { this.backend = backend; } - @Override - public int priority() { - return 150; - } - @Override public String key() { return "permission-provider"; diff --git a/src/main/java/dev/coph/flightscore/backend/user/role/RoleProvider.java b/src/main/java/dev/coph/flightscore/backend/user/role/RoleProvider.java index 8ae336b..ddb98ab 100644 --- a/src/main/java/dev/coph/flightscore/backend/user/role/RoleProvider.java +++ b/src/main/java/dev/coph/flightscore/backend/user/role/RoleProvider.java @@ -14,6 +14,7 @@ import lombok.experimental.Accessors; import java.util.HashMap; import java.util.List; +import java.util.Set; @Accessors(fluent = true) public class RoleProvider implements Provider { @@ -29,8 +30,8 @@ public class RoleProvider implements Provider { @Override - public int priority() { - return 100; + public Set dependsOn() { + return Set.of("permission-provider"); } @Override