Skip to content

Commit bd2af0c

Browse files
authored
GH-92 Improve the general plugin security, code style and performance.
2 parents b97ce7e + 1890e92 commit bd2af0c

File tree

103 files changed

+3528
-2098
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

103 files changed

+3528
-2098
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
#Mon Dec 23 12:09:35 CET 2024
1+
#Fri Apr 04 19:27:25 CEST 2025
22
distributionBase=GRADLE_USER_HOME
33
distributionPath=wrapper/dists
4-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
4+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
55
zipStoreBase=GRADLE_USER_HOME
66
zipStorePath=wrapper/dists

spenttime-api/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ version = "2.0.0"
55

66
dependencies {
77
implementation("com.github.ben-manes.caffeine:caffeine:3.1.8")
8+
implementation("org.jetbrains:annotations:26.0.2")
89
}
910

1011
tasks.withType<ShadowJar> {
Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
11
package com.github.imdmk.spenttime;
22

33
import com.github.imdmk.spenttime.user.UserCache;
4-
import com.github.imdmk.spenttime.user.UserService;
54
import com.github.imdmk.spenttime.user.repository.UserRepository;
5+
import org.jetbrains.annotations.NotNull;
66

7+
/**
8+
* Main API interface for accessing user-related components of the SpentTime module.
9+
* <p>
10+
* Provides access to:
11+
* - {@link UserCache}: in-memory user cache (by UUID and name)
12+
* - {@link UserRepository}: persistence layer for users
13+
*/
714
public interface SpentTimeApi {
815

9-
UserCache getUserCache();
10-
11-
UserService getUserService();
12-
13-
UserRepository getUserRepository();
16+
/**
17+
* Returns the in-memory user cache.
18+
*
19+
* @return {@link UserCache} the user cache instance
20+
*/
21+
@NotNull UserCache getUserCache();
1422

23+
/**
24+
* Returns the user repository for data access operations.
25+
*
26+
* @return {@link UserRepository} the user repository
27+
*/
28+
@NotNull UserRepository getUserRepository();
1529
}

spenttime-api/src/main/java/com/github/imdmk/spenttime/SpentTimeApiProvider.java

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
package com.github.imdmk.spenttime;
22

3+
import org.jetbrains.annotations.NotNull;
4+
5+
/**
6+
* Static access point for the {@link SpentTimeApi}.
7+
* Acts as a global registry for the current instance.
8+
* <p>
9+
* Not thread-safe.
10+
*/
311
public class SpentTimeApiProvider {
412

513
private static SpentTimeApi SPENT_TIME_API;
@@ -8,25 +16,49 @@ private SpentTimeApiProvider() {
816
throw new UnsupportedOperationException("This class cannot be instantiated.");
917
}
1018

11-
public static SpentTimeApi get() {
19+
/**
20+
* Returns the registered {@link SpentTimeApi}.
21+
*
22+
* @return the registered API
23+
* @throws IllegalStateException if the API is not yet registered
24+
*/
25+
public synchronized static SpentTimeApi get() {
1226
if (SPENT_TIME_API == null) {
13-
throw new IllegalStateException("The DiscordIntegrationAPI isn't registered.");
27+
throw new IllegalStateException("The SpentTimeApi isn't registered.");
1428
}
1529

1630
return SPENT_TIME_API;
1731
}
1832

19-
static void register(SpentTimeApi spentTimeApi) {
33+
/**
34+
* Registers the {@link SpentTimeApi} instance.
35+
*
36+
* @param spentTimeApi the API instance to register
37+
* @throws IllegalStateException if already registered
38+
*/
39+
static synchronized void register(@NotNull SpentTimeApi spentTimeApi) {
2040
if (SPENT_TIME_API != null) {
21-
throw new IllegalStateException("The DiscordIntegrationAPI is already registered.");
41+
throw new IllegalStateException("The SpentTimeApi is already registered.");
2242
}
2343

2444
SPENT_TIME_API = spentTimeApi;
2545
}
2646

27-
static void unregister() {
47+
/**
48+
* Forces to register the {@link SpentTimeApi} instance.
49+
*/
50+
static void forceRegister(@NotNull SpentTimeApi api) {
51+
SPENT_TIME_API = api;
52+
}
53+
54+
/**
55+
* Unregisters the {@link SpentTimeApi}.
56+
*
57+
* @throws IllegalStateException if no API was registered
58+
*/
59+
static synchronized void unregister() {
2860
if (SPENT_TIME_API == null) {
29-
throw new IllegalStateException("The DiscordIntegrationAPI isn't registered.");
61+
throw new IllegalStateException("The SpentTimeApi isn't registered.");
3062
}
3163

3264
SPENT_TIME_API = null;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.github.imdmk.spenttime.configuration;
2+
3+
import eu.okaeri.configs.OkaeriConfig;
4+
import eu.okaeri.configs.serdes.OkaeriSerdesPack;
5+
import org.jetbrains.annotations.NotNull;
6+
7+
public abstract class ConfigSection extends OkaeriConfig {
8+
9+
public abstract @NotNull OkaeriSerdesPack getSerdesPack();
10+
11+
public abstract @NotNull String getFileName();
12+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.github.imdmk.spenttime.configuration;
2+
3+
public class ConfigurationLoadException extends RuntimeException {
4+
public ConfigurationLoadException(Throwable cause) {
5+
super("Failed to load configuration", cause);
6+
}
7+
8+
public ConfigurationLoadException(String message) {
9+
super(message);
10+
}
11+
12+
public ConfigurationLoadException(String message, Throwable cause) {
13+
super(message, cause);
14+
}
15+
16+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package com.github.imdmk.spenttime.configuration;
2+
3+
import eu.okaeri.configs.ConfigManager;
4+
import eu.okaeri.configs.OkaeriConfig;
5+
import eu.okaeri.configs.exception.OkaeriException;
6+
import eu.okaeri.configs.yaml.snakeyaml.YamlSnakeYamlConfigurer;
7+
import org.jetbrains.annotations.NotNull;
8+
import org.yaml.snakeyaml.DumperOptions;
9+
import org.yaml.snakeyaml.LoaderOptions;
10+
import org.yaml.snakeyaml.Yaml;
11+
import org.yaml.snakeyaml.constructor.Constructor;
12+
import org.yaml.snakeyaml.representer.Representer;
13+
import org.yaml.snakeyaml.resolver.Resolver;
14+
15+
import java.io.File;
16+
import java.util.Objects;
17+
import java.util.Set;
18+
import java.util.concurrent.CompletableFuture;
19+
import java.util.concurrent.ConcurrentHashMap;
20+
import java.util.concurrent.ExecutorService;
21+
import java.util.concurrent.Executors;
22+
import java.util.logging.Level;
23+
import java.util.logging.Logger;
24+
25+
public final class ConfigurationManager {
26+
27+
private final Set<ConfigSection> configs = ConcurrentHashMap.newKeySet();
28+
29+
private final Logger logger;
30+
private final ExecutorService executor;
31+
32+
public ConfigurationManager(@NotNull Logger logger) {
33+
this.logger = Objects.requireNonNull(logger, "logger cannot be null");
34+
this.executor = Executors.newSingleThreadExecutor();
35+
}
36+
37+
public <T extends ConfigSection> T create(@NotNull Class<T> config, @NotNull File dataFolder) {
38+
T configFile = ConfigManager.create(config);
39+
File file = new File(dataFolder, configFile.getFileName());
40+
41+
YamlSnakeYamlConfigurer yamlSnakeYamlConfigurer = this.createYamlSnakeYamlConfigurer();
42+
43+
configFile.withConfigurer(yamlSnakeYamlConfigurer);
44+
configFile.withSerdesPack(configFile.getSerdesPack());
45+
configFile.withBindFile(file);
46+
configFile.withRemoveOrphans(true);
47+
configFile.saveDefaults();
48+
configFile.load(true);
49+
50+
this.configs.add(configFile);
51+
52+
return configFile;
53+
}
54+
55+
private @NotNull YamlSnakeYamlConfigurer createYamlSnakeYamlConfigurer() {
56+
LoaderOptions loaderOptions = new LoaderOptions();
57+
Constructor constructor = new Constructor(loaderOptions);
58+
59+
DumperOptions dumperOptions = new DumperOptions();
60+
dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.AUTO);
61+
dumperOptions.setIndent(2);
62+
dumperOptions.setSplitLines(false);
63+
64+
Representer representer = new CustomRepresenter(dumperOptions);
65+
Resolver resolver = new Resolver();
66+
67+
Yaml yaml = new Yaml(constructor, representer, dumperOptions, loaderOptions, resolver);
68+
return new YamlSnakeYamlConfigurer(yaml);
69+
}
70+
71+
public @NotNull CompletableFuture<Void> reloadAll() {
72+
return CompletableFuture.runAsync(this::loadAll, this.executor);
73+
}
74+
75+
private void loadAll() {
76+
this.configs.forEach(this::load);
77+
}
78+
79+
public void load(@NotNull OkaeriConfig config) {
80+
try {
81+
config.load(true);
82+
}
83+
catch (OkaeriException exception) {
84+
this.logger.log(Level.SEVERE, "Failed to load config: " + config.getClass().getSimpleName(), exception);
85+
throw new ConfigurationLoadException(exception);
86+
}
87+
}
88+
89+
public void shutdown() {
90+
this.logger.info("Shutting down ConfigurationManager executor");
91+
this.executor.shutdownNow();
92+
}
93+
94+
}

spenttime-api/src/main/java/com/github/imdmk/spenttime/configuration/CustomRepresenter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import java.util.LinkedHashMap;
1212
import java.util.Map;
1313

14-
public class CustomRepresenter extends Representer {
14+
public final class CustomRepresenter extends Representer {
1515

1616
public CustomRepresenter(DumperOptions options) {
1717
super(options);

spenttime-api/src/main/java/com/github/imdmk/spenttime/database/DatabaseSettings.java renamed to spenttime-api/src/main/java/com/github/imdmk/spenttime/database/DatabaseConfiguration.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package com.github.imdmk.spenttime.database;
22

3-
import eu.okaeri.configs.OkaeriConfig;
3+
import com.github.imdmk.spenttime.configuration.ConfigSection;
44
import eu.okaeri.configs.annotation.Comment;
5+
import eu.okaeri.configs.serdes.OkaeriSerdesPack;
6+
import org.jetbrains.annotations.NotNull;
57

6-
public class DatabaseSettings extends OkaeriConfig {
8+
public class DatabaseConfiguration extends ConfigSection {
79

810
@Comment({
911
"# Database mode",
@@ -16,4 +18,14 @@ public class DatabaseSettings extends OkaeriConfig {
1618
public String username = "root";
1719
public String password = "ExamplePassword1101";
1820
public int port = 3306;
21+
22+
@Override
23+
public @NotNull OkaeriSerdesPack getSerdesPack() {
24+
return registry -> {};
25+
}
26+
27+
@Override
28+
public @NotNull String getFileName() {
29+
return "databaseConfiguration.yml";
30+
}
1931
}

0 commit comments

Comments
 (0)