Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions networkdataapi-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
<artifactId>mongodb-driver-sync</artifactId>
</dependency>

<!-- Redis (Jedis) -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>

<!-- Caffeine Cache -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import com.astroid.stijnjakobs.networkdataapi.core.config.ConfigurationManager;
import com.astroid.stijnjakobs.networkdataapi.core.database.DatabaseManager;
import com.astroid.stijnjakobs.networkdataapi.core.environment.EnvironmentDetector;
import com.astroid.stijnjakobs.networkdataapi.core.redis.RedisManager;
import com.astroid.stijnjakobs.networkdataapi.core.rest.RESTApiService;
import com.astroid.stijnjakobs.networkdataapi.core.service.PlayerDataService;
import com.astroid.stijnjakobs.networkdataapi.core.service.RedisDataService;
import lombok.Getter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -44,9 +46,11 @@ public class CoreManager {

private ConfigurationManager configurationManager;
private DatabaseManager databaseManager;
private RedisManager redisManager;
private CacheManager cacheManager;
private AsyncExecutor asyncExecutor;
private PlayerDataService playerDataService;
private RedisDataService redisDataService;
private RESTApiService restApiService;

private boolean initialized = false;
Expand Down Expand Up @@ -90,6 +94,19 @@ public void initialize(File dataFolder) throws Exception {
databaseManager = new DatabaseManager(configurationManager);
databaseManager.connect();

// Initialize Redis manager (if enabled)
if (configurationManager.getBoolean("redis.enabled", false)) {
logger.info("Connecting to Redis...");
redisManager = new RedisManager(configurationManager);
redisManager.connect();

// Initialize Redis data service
logger.info("Initializing Redis data service...");
redisDataService = new RedisDataService(redisManager, asyncExecutor);
} else {
logger.info("Redis is disabled in configuration");
}

// Initialize services
logger.info("Initializing player data service...");
playerDataService = new PlayerDataService(databaseManager, cacheManager, asyncExecutor);
Expand Down Expand Up @@ -132,6 +149,15 @@ private void scheduleMaintenanceTasks() {
databaseManager.reconnect();
}
}, 1, TimeUnit.MINUTES);

// Redis health check every minute (if enabled)
if (redisManager != null) {
asyncExecutor.schedule(() -> {
if (!redisManager.isAlive()) {
logger.warn("Redis health check failed");
}
}, 1, TimeUnit.MINUTES);
Comment on lines +154 to +159
Copy link

Copilot AI Nov 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Redis health check is scheduled but won't repeat automatically. The schedule method schedules a one-time execution after 1 minute. To make this a recurring health check like the database health check, use scheduleAtFixedRate or scheduleWithFixedDelay instead. For example: asyncExecutor.scheduleAtFixedRate(() -> { ... }, 1, 1, TimeUnit.MINUTES);

Copilot uses AI. Check for mistakes.
}
}

/**
Expand Down Expand Up @@ -163,7 +189,16 @@ public void shutdown() {
try {
databaseManager.shutdown();
} catch (Exception e) {
logger.error("Error closing database connection", e);
logger.error("Error shutting down database", e);
Copy link

Copilot AI Nov 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The error message was changed from "Error closing database connection" to "Error shutting down database". While this is more consistent with the new Redis shutdown message, this is a pre-existing line that was modified. Consider whether this change should be in a separate commit focused on consistency improvements, as it's not directly related to Redis integration.

Copilot uses AI. Check for mistakes.
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

}
}

// Close Redis connection
if (redisManager != null) {
try {
redisManager.shutdown();
} catch (Exception e) {
logger.error("Error shutting down Redis", e);
}
}

Expand All @@ -188,5 +223,77 @@ public void shutdown() {
public boolean isInitialized() {
return initialized;
}

/**
* Gets the configuration manager.
*
* @return the configuration manager
*/
public ConfigurationManager getConfigurationManager() {
return configurationManager;
}

/**
* Gets the database manager.
*
* @return the database manager
*/
public DatabaseManager getDatabaseManager() {
return databaseManager;
}

/**
* Gets the Redis manager.
*
* @return the Redis manager
*/
public RedisManager getRedisManager() {
return redisManager;
}

/**
* Gets the cache manager.
*
* @return the cache manager
*/
public CacheManager getCacheManager() {
return cacheManager;
}

/**
* Gets the async executor.
*
* @return the async executor
*/
public AsyncExecutor getAsyncExecutor() {
return asyncExecutor;
}

/**
* Gets the player data service.
*
* @return the player data service
*/
public PlayerDataService getPlayerDataService() {
return playerDataService;
}

/**
* Gets the Redis data service.
*
* @return the Redis data service
*/
public RedisDataService getRedisDataService() {
return redisDataService;
}

/**
* Gets the REST API service.
*
* @return the REST API service
*/
public RESTApiService getRestApiService() {
return restApiService;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.astroid.stijnjakobs.networkdataapi.core.CoreManager;
import com.astroid.stijnjakobs.networkdataapi.core.service.PlayerDataService;
import com.astroid.stijnjakobs.networkdataapi.core.service.RedisDataService;
import redis.clients.jedis.JedisPool;

/**
* Public API registry for accessing NetworkDataAPI services.
Expand Down Expand Up @@ -107,5 +109,24 @@ public String getVersion() {
public boolean isHealthy() {
return coreManager.getDatabaseManager().isHealthy();
}

@Override
public RedisDataService getRedisDataService() {
return coreManager.getRedisDataService();
}

@Override
public JedisPool getRedisPool() {
if (coreManager.getRedisManager() == null) {
return null;
}
return coreManager.getRedisManager().getJedisPool();
}

@Override
public boolean isRedisEnabled() {
return coreManager.getRedisManager() != null &&
coreManager.getRedisManager().isConnected();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.astroid.stijnjakobs.networkdataapi.core.api;

import com.astroid.stijnjakobs.networkdataapi.core.service.PlayerDataService;
import com.astroid.stijnjakobs.networkdataapi.core.service.RedisDataService;
import com.mongodb.client.MongoDatabase;
import redis.clients.jedis.JedisPool;

/**
* Public API interface for NetworkDataAPI.
Expand Down Expand Up @@ -139,4 +141,76 @@ public interface NetworkDataAPIProvider {
* @return true if healthy, false otherwise
*/
boolean isHealthy();

/**
* Gets the Redis data service for caching and messaging.
*
* <p>The Redis data service provides methods for:</p>
* <ul>
* <li>String operations (get, set, with TTL)</li>
* <li>Hash operations (field-value storage)</li>
* <li>Set operations (unique members)</li>
* <li>List operations (ordered data)</li>
* <li>Pub/Sub messaging</li>
* <li>Counter operations</li>
* </ul>
*
* <p><strong>Example - Caching player data:</strong></p>
* <pre>{@code
* RedisDataService redis = api.getRedisDataService();
*
* // Cache with 5 minute TTL
* redis.setWithExpiry("player:" + uuid, playerData, 300);
*
* // Retrieve cached data
* String data = redis.get("player:" + uuid);
* }</pre>
*
* <p><strong>Example - Pub/Sub messaging:</strong></p>
* <pre>{@code
* // Publish to other servers
* redis.publish("player-join", uuid.toString());
* }</pre>
*
* @return the Redis data service, or null if Redis is disabled
*/
RedisDataService getRedisDataService();

/**
* Gets direct access to the Redis connection pool.
*
* <p>This allows plugins to use the shared Redis connection pool
* for custom operations not covered by RedisDataService.</p>
*
* <p><strong>Example - Custom Redis operations:</strong></p>
* <pre>{@code
* JedisPool pool = api.getRedisPool();
* try (Jedis jedis = pool.getResource()) {
* // Custom Redis commands
* jedis.zadd("leaderboard", 1000, "player1");
* Set<String> top10 = jedis.zrevrange("leaderboard", 0, 9);
* }
* }</pre>
*
* <p><strong>Benefits:</strong></p>
* <ul>
* <li>No separate Redis connection needed</li>
* <li>Uses shared connection pool (efficient)</li>
* <li>Automatic reconnection</li>
* <li>Full Jedis API access</li>
* </ul>
*
* <p><strong>Important:</strong> Always use try-with-resources to ensure
* connections are returned to the pool!</p>
*
* @return the Jedis pool, or null if Redis is disabled
*/
JedisPool getRedisPool();

/**
* Checks if Redis is enabled and connected.
*
* @return true if Redis is available
*/
boolean isRedisEnabled();
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,26 @@ private void createDefaultConfig() throws IOException {
max-connection-idle-time-ms: 60000
max-connection-life-time-ms: 600000

# Redis Connection Settings
redis:
enabled: false
host: "localhost"
port: 6379
Copy link

Copilot AI Nov 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The password configuration uses an empty string "" as the default value. This might be interpreted differently than null. In RedisManager line 108, there's a check for password != null && !password.isEmpty(), which correctly handles this case. However, for clarity, consider using null or adding a comment explaining that an empty string means "no password".

Suggested change
port: 6379
port: 6379
# Leave empty ("") for no password

Copilot uses AI. Check for mistakes.
password: ""
database: 0
timeout-ms: 2000
# Connection pool settings
max-pool-size: 100
max-idle: 50
min-idle: 10
test-on-borrow: true
test-on-return: false
test-while-idle: true
min-evictable-idle-time-ms: 60000
time-between-eviction-runs-ms: 30000
block-when-exhausted: true
max-wait-ms: 3000

# Cache Settings
cache:
enabled: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,8 @@ public class DatabaseManager {

private MongoClient mongoClient;

@Getter
private MongoDatabase database;

@Getter
private boolean connected = false;

private final ConfigurationManager config;
Expand All @@ -58,6 +56,24 @@ public DatabaseManager(ConfigurationManager config) {
this.config = config;
}

/**
* Gets the MongoDB database instance.
*
* @return the MongoDatabase instance
*/
public MongoDatabase getDatabase() {
return database;
}

/**
* Checks if the database is connected.
*
* @return true if connected, false otherwise
*/
public boolean isConnected() {
return connected;
}

/**
* Initializes the database connection.
*
Expand Down
Loading
Loading