Skip to content

Commit f2c857a

Browse files
authored
Merge pull request #5 from HauntedMC/2.0
Update DataProvider backend and API to v2.0.0
2 parents aa85847 + a82d733 commit f2c857a

File tree

89 files changed

+4943
-1085
lines changed

Some content is hidden

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

89 files changed

+4943
-1085
lines changed

README.md

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
[![License](https://img.shields.io/github/license/HauntedMC/dataprovider)](LICENSE)
77
[![Java 21](https://img.shields.io/badge/Java-21-007396)](https://adoptium.net/)
88

9-
Build plugin features, not database plumbing.
9+
Build plugins and services, not database plumbing.
1010

1111
`DataProvider` is shared infrastructure for plugin developers on Velocity and Bukkit/Paper.
1212
It gives you one clean API for MySQL, MongoDB, Redis, and Redis messaging so your plugin code can stay focused on gameplay and business logic.
@@ -19,12 +19,11 @@ It gives you one clean API for MySQL, MongoDB, Redis, and Redis messaging so you
1919
- Cleaner codebase: typed APIs reduce casting and repetitive boilerplate.
2020
- Better runtime behavior: connection reuse and lifecycle cleanup are handled centrally.
2121

22-
## What You Get
22+
## Features
2323

24-
- Unified backend support: `MYSQL`, `MONGODB`, `REDIS`, `REDIS_MESSAGING`
24+
- Following data backends are implemented: `MYSQL`, `MONGODB`, `REDIS`, `REDIS_MESSAGING`
2525
- Platform support: Velocity + Bukkit/Paper
26-
- Reference-counted connection lifecycle management
27-
- Optional ORM support for relational workflows (`ORMContext`)
26+
- Optional ORM (through hibernate) support for relational workflows (`ORMContext`)
2827

2928
## Requirements
3029

@@ -34,9 +33,32 @@ It gives you one clean API for MySQL, MongoDB, Redis, and Redis messaging so you
3433

3534
## Quick Start
3635

36+
Resolve the API from your platform runtime:
37+
38+
Velocity:
39+
3740
```java
38-
DataProviderAPI api = VelocityDataProvider.getDataProviderAPI();
41+
DataProviderAPI api = proxyServer.getPluginManager()
42+
.getPlugin("dataprovider")
43+
.flatMap(container -> container.getInstance()
44+
.filter(DataProviderApiSupplier.class::isInstance)
45+
.map(DataProviderApiSupplier.class::cast)
46+
.map(DataProviderApiSupplier::dataProviderApi))
47+
.orElseThrow(() -> new IllegalStateException("DataProvider is unavailable."));
48+
```
49+
50+
Bukkit/Paper:
51+
52+
```java
53+
RegisteredServiceProvider<DataProviderAPI> registration =
54+
Bukkit.getServicesManager().getRegistration(DataProviderAPI.class);
55+
if (registration == null) {
56+
return;
57+
}
58+
DataProviderAPI api = registration.getProvider();
59+
```
3960

61+
```java
4062
Optional<RelationalDatabaseProvider> mysql = api.registerDatabaseAs(
4163
DatabaseType.MYSQL,
4264
"default",
@@ -53,6 +75,19 @@ api.unregisterDatabase(DatabaseType.MYSQL, "default");
5375

5476
If you maintain multiple plugins, this gives your team one standard integration model instead of backend-specific code per project.
5577

78+
## Admin Commands
79+
80+
- `/dataprovider help` shows command usage.
81+
- `/dataprovider status [summary|connections] [unhealthy] [plugin <name>] [type <databaseType>]` shows active connection diagnostics.
82+
- `/dataprovider config` prints current runtime config state (`orm.schema_mode` + backend enablement).
83+
- `/dataprovider reload` reloads `config.yml` from disk.
84+
85+
Permissions:
86+
87+
- `dataprovider.command.status`
88+
- `dataprovider.command.config`
89+
- `dataprovider.command.reload`
90+
5691
## Install DataProvider (Server)
5792

5893
1. Build or download `DataProvider.jar`.

SECURITY.md

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@
22

33
## Supported Versions
44

5-
Security fixes are generally provided for the latest stable release line.
5+
Security fixes are prioritized for the latest stable release line.
6+
Older versions may receive fixes on a best-effort basis.
67

7-
| Version | Supported |
8-
| --- | --- |
9-
| Latest stable release | Yes |
10-
| Older releases | Best effort / No guarantee |
118

129
## Reporting a Vulnerability
1310

@@ -25,11 +22,13 @@ Include:
2522
- Impact assessment
2623
- Any proposed mitigation
2724

28-
## Response Expectations
2925

30-
- Initial triage acknowledgement: target within 72 hours
31-
- Severity assessment and fix planning: as soon as reproducible
32-
- Patch release timing: based on severity and exploitability
26+
## What to Expect
27+
28+
- We acknowledge reports as quickly as practical.
29+
- We validate impact, prioritize by severity, and prepare a fix.
30+
- We coordinate disclosure after a fix or mitigation is available.
31+
3332

3433
## Disclosure
3534

docs/ARCHITECTURE.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ Main modules:
1010
- `api`: public registration/lookup surface
1111
- `internal`: registry, factory, config mapping, identity, and lifecycle logic
1212
- `database.*`: backend implementations and typed data-access contracts
13-
- `platform.bukkit` / `platform.velocity`: platform bootstrap and caller context resolution
13+
- `platform.internal`: shared platform runtime lifecycle and command behavior
14+
- `platform.bukkit` / `platform.velocity`: platform adapters (bootstrap, command wiring, caller context resolution)
1415

1516
## Registration Model
1617

@@ -30,9 +31,22 @@ Main modules:
3031
## Lifecycle Safety
3132

3233
- Per-caller ownership checks gate unregister operations.
34+
- Reference ownership is tracked by owner scope.
35+
- Default API methods use plugin-level owner scope for predictable lifecycle behavior.
36+
- If one plugin/software process multiplexes multiple components through one wrapper class, use optional scoped lifecycle facades (`DataProviderAPI.scope(...)`) to preserve component isolation.
37+
- Explicit plugin-wide cleanup is available for shutdown flows that span multiple caller scopes.
3338
- Stale/disconnected providers are evicted from registry lookup paths.
3439
- Shutdown hooks unregister or stop backend resources cleanly.
3540
- Bounded executors are used for asynchronous backend work queues.
41+
- Platform runtime wrappers use a shared thread-safe lifecycle holder to prevent stale instance leaks across enable/disable cycles.
42+
43+
## Platform Layer Design
44+
45+
- `PlatformDataProviderRuntime` centralizes bootstrap shutdown behavior and startup rollback handling.
46+
- Platform command adapters delegate to a shared `DataProviderCommandService` so Bukkit and Velocity command behavior stays identical.
47+
- Command service exposes diagnostics-focused admin commands (`status`, `config`, `reload`) with permission-gated filtering and runtime health summaries.
48+
- API discovery is platform-native: Bukkit registers `DataProviderAPI` in `ServicesManager`; Velocity exposes `DataProviderApiSupplier` on plugin instance.
49+
- Platform-specific wrappers only map host APIs to shared internals (logger, command registration, event/plugin lifecycle hooks).
3650

3751
## ORM Integration
3852

docs/BEST_PRACTICES.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,30 @@
33
## API usage
44

55
- Prefer `registerDatabaseOptional`, `registerDatabaseAs`, and `registerDataAccess` over raw nullable APIs.
6+
- Treat returned `DatabaseProvider` instances as read-only handles; lifecycle is managed through the API.
67
- Prefer `getDataAccessOptional(...)` instead of manual casts.
78
- Treat database registration as startup wiring, not ad-hoc runtime behavior in hot paths.
89

910
## Lifecycle
1011

11-
- Register once during feature/plugin init.
12+
- Register once during plugin/software startup.
1213
- Unregister on disable.
13-
- If you run multiple feature modules in one plugin, prefer releasing only the connections each feature acquired.
14-
- Use `unregisterAllDatabases()` only when shutting down the entire plugin context.
14+
- `registerDatabase(...)` / `unregisterAllDatabases()` use the default plugin-level owner scope.
15+
- For full plugin/software shutdown across multiple scopes/classes, use `unregisterAllDatabasesForPlugin()`.
16+
17+
## Optional Scoped Ownership
18+
19+
- Use scoped ownership only when one plugin/software process has independently managed components.
20+
- Create a scope facade from `DataProviderAPI.scope("component.name")`.
21+
- Register and release through that scope object so ownership remains isolated.
22+
- Keep scope naming stable and deterministic.
1523

1624
## Messaging
1725

1826
- Use one clear message class per channel contract.
1927
- Keep channels stable and namespaced (for example: `proxy.staffchat.message`).
2028
- Handle parse/dispatch failures as non-fatal.
29+
- Keep handlers fast and non-blocking; use `security.max_queued_messages_per_handler` to cap per-handler backlog.
2130

2231
## ORM
2332

docs/CONFIGURATION.md

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,55 +44,89 @@ databases:
4444
- `require_secure_transport`
4545
- `allow_public_key_retrieval`
4646
- `pool_size`
47+
- `min_idle`
4748
- `queue_capacity`
49+
- `connection_timeout_ms`
50+
- `validation_timeout_ms`
51+
- `idle_timeout_ms`
52+
- `max_lifetime_ms`
53+
- `leak_detection_threshold_ms`
54+
- `connect_timeout_ms`
55+
- `socket_timeout_ms`
56+
- `query_timeout_seconds`
57+
- `default_fetch_size`
58+
- `cache_prepared_statements`
59+
- `prepared_statement_cache_size`
60+
- `prepared_statement_cache_sql_limit`
4861

4962
## MongoDB Keys (`databases/mongodb.yml`)
5063

5164
- `host`, `port`, `database`, `username`, `password`
5265
- `authSource` (note exact casing)
5366
- `require_secure_transport`
5467
- `tls.enabled`
55-
- `tls.allow_invalid_hostnames` (deprecated, ignored for security)
56-
- `tls.trust_all_certificates` (deprecated, ignored for security)
68+
- `tls.allow_invalid_hostnames` (must remain `false`; startup fails otherwise)
69+
- `tls.trust_all_certificates` (must remain `false`; startup fails otherwise)
5770
- `tls.trust_store_path` (optional JKS/PKCS12 path for private CA/self-managed trust)
5871
- `tls.trust_store_password` (optional trust store password)
5972
- `tls.trust_store_type` (optional, defaults to JVM `KeyStore.getDefaultType()`)
6073
- `pool_size`
6174
- `queue_capacity`
75+
- `max_connection_pool_size`
76+
- `min_connection_pool_size`
77+
- `connect_timeout_ms`
78+
- `socket_timeout_ms`
79+
- `server_selection_timeout_ms`
6280

6381
## Redis Keys (`databases/redis.yml`)
6482

6583
- `host`, `port`, `user`, `password`, `database`
6684
- `require_secure_transport`
6785
- `tls.enabled`
68-
- `tls.verify_hostname` (deprecated, ignored for security)
69-
- `tls.trust_all_certificates` (deprecated, ignored for security)
86+
- `tls.verify_hostname` (must remain `true`; startup fails otherwise)
87+
- `tls.trust_all_certificates` (must remain `false`; startup fails otherwise)
7088
- `tls.trust_store_path` (optional JKS/PKCS12 path for private CA/self-managed trust)
7189
- `tls.trust_store_password` (optional trust store password)
7290
- `tls.trust_store_type` (optional, defaults to JVM `KeyStore.getDefaultType()`)
7391
- `pool.connections`
7492
- `pool.threads`
75-
- `queue_capacity`
93+
- `pool.min_idle`
94+
- `pool.max_idle`
95+
- `pool.test_on_borrow`
96+
- `pool.test_while_idle`
97+
- `pool.queue_capacity`
98+
- `connection_timeout_ms`
99+
- `socket_timeout_ms`
100+
- `scan_count`
101+
- `security.max_scan_results`
76102

77103
## Redis Messaging Keys (`databases/redis_messaging.yml`)
78104

79105
- Same network + TLS fields as Redis key-value
80106
- `pool.connections`
81107
- `pool.threads`
108+
- `pool.min_idle`
109+
- `pool.max_idle`
110+
- `pool.test_on_borrow`
111+
- `pool.test_while_idle`
82112
- `pool.queue_capacity`
83113
- `pool.max_subscriptions`
114+
- `connection_timeout_ms`
115+
- `socket_timeout_ms`
84116
- `security.max_payload_chars`
117+
- `security.max_queued_messages_per_handler` (per-subscriber queue cap to isolate slow handlers)
85118

86119
## Common Mistakes
87120

88121
- Identifier mismatch between code and config section names
89122
- Enabling TLS flags without server-side TLS support
90-
- Using deprecated insecure TLS flags instead of a trust store for private CA deployments
91-
- Assuming Redis and Redis Messaging use identical pool key paths (`queue_capacity` differs)
123+
- Setting insecure TLS flags (`trust_all_certificates`, `allow_invalid_hostnames`, or `verify_hostname=false`) which now fail startup in 2.0
124+
- Using `queue_capacity` at the root of `redis.yml` instead of `pool.queue_capacity`
92125

93126
## Operational Notes
94127

95128
- Use `default` for single-backend setups.
96129
- Use explicit identifiers (for example `rw`, `ro`, `analytics`) for multi-backend setups.
97130
- Validate trust store configuration in staging before production rollout.
98131
- Never commit production credentials.
132+
- During plugin shutdown across many classes/scopes, pair cleanup with `unregisterAllDatabasesForPlugin()`.

docs/DEVELOPMENT.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ mvn -B package
2323
- `src/main/java/nl/hauntedmc/dataprovider/api`: public API contracts
2424
- `src/main/java/nl/hauntedmc/dataprovider/internal`: registry/factory/config internals
2525
- `src/main/java/nl/hauntedmc/dataprovider/database`: backend implementations
26+
- `src/main/java/nl/hauntedmc/dataprovider/logging`: backend-agnostic logging contracts + adapters
27+
- `src/main/java/nl/hauntedmc/dataprovider/platform/internal`: shared platform lifecycle + command behavior
2628
- `src/main/java/nl/hauntedmc/dataprovider/platform`: Bukkit/Velocity adapters
2729
- `src/test/java`: unit tests by package
2830

@@ -32,6 +34,7 @@ mvn -B package
3234
- Keep connection registration in startup lifecycle paths, not request hot paths.
3335
- Handle external IO failures as non-fatal where possible and log actionable context.
3436
- Keep platform-specific integration (`platform.bukkit`, `platform.velocity`) thin and isolated.
37+
- Put cross-platform wrapper behavior in `platform.internal` before adding platform-local duplication.
3538
- Avoid leaking plugin context across module boundaries.
3639

3740
## Manual Validation Checklist

docs/RELEASE.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ Use `update_version.sh` to bump `major`, `minor`, or `patch`:
2121

2222
The script updates:
2323

24-
- `version.txt`
25-
- `pom.xml`
26-
- `README.md` (version examples)
24+
- `pom.xml` (via Maven `versions:set`; source of truth)
2725
- `src/main/java/nl/hauntedmc/dataprovider/platform/velocity/VelocityDataProvider.java`
2826

27+
Manual step:
28+
29+
- Update README dependency version examples if needed.
30+
2931
Then it commits and tags (`vX.Y.Z`) locally.
3032
Push when ready:
3133

docs/SCOPED_LIFECYCLE.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Scoped Lifecycle (Optional)
2+
3+
`DataProviderScope` is an advanced ownership option.
4+
Use it only when one plugin/software process contains multiple independently managed components.
5+
6+
## Create a Scope
7+
8+
```java
9+
DataProviderScope chatScope = api.scope("component.chat");
10+
```
11+
12+
You can also use a typed scope object:
13+
14+
```java
15+
OwnerScope chatOwner = OwnerScope.of("component.chat");
16+
DataProviderScope chatScope = api.scope(chatOwner);
17+
```
18+
19+
Scope names must be stable, non-blank, and use safe identifier characters.
20+
21+
## Register Through the Scope
22+
23+
```java
24+
Optional<MessagingDataAccess> bus = chatScope.registerDataAccess(
25+
DatabaseType.REDIS_MESSAGING,
26+
"hauntedmc",
27+
MessagingDataAccess.class
28+
);
29+
```
30+
31+
## Release Only That Scope
32+
33+
```java
34+
chatScope.unregisterAllDatabases();
35+
```
36+
37+
`DataProviderScope` is `AutoCloseable`, so it can also be used with try-with-resources:
38+
39+
```java
40+
try (DataProviderScope tempScope = api.scope("component.temp")) {
41+
tempScope.registerDatabase(DatabaseType.MYSQL, "default");
42+
}
43+
```
44+
45+
## Full Plugin/Process Shutdown
46+
47+
Scope cleanup is targeted.
48+
For deterministic full shutdown across all scopes, call:
49+
50+
```java
51+
api.unregisterAllDatabasesForPlugin();
52+
```

0 commit comments

Comments
 (0)