A complete, in-memory JavaFX desktop simulator that models how cloud gateways, fintech systems and large-scale web services meter API traffic, enforce rate-limit policies, detect abusive behaviour and generate security reports — zero network, zero server, 100% OOP. This page is the full A→Z documentation, architecture graph and presentation deck for the entire codebase.
18 chapters — 12 core + 6 reference. Click any card to jump — or press ▶ Presentation for a 14-slide deck (arrow keys / Esc).
The problem being simulated, the solution built, and the exact boundary of scope.
In high-throughput distributed systems thousands of clients hit the same endpoints concurrently. Without control you get resource exhaustion, DoS, credential stuffing, bot scraping and unfair usage. Production gateways (Kong, AWS API Gateway, NGINX, Cloudflare) solve this with metering + policy + anomaly engines.
This project rebuilds that internal logical behaviour as a desktop app: every request is an object, every rule is a policy object, every verdict is a report — all executed in memory with the Java Time API and collections.
Mandatory features delivered: register API clients → record simulated requests → apply rate-limit policies → detect excessive requests inside time windows → generate a per-client abuse report.
Track client request activity → apply rate-limiting rules → detect abnormal/abusive patterns → generate a per-client usage & violation report.
Domain modelling, encapsulation of counters/thresholds, polymorphic policy evaluation, Strategy-like design, type-safe Enums.
No sockets, no latency, no external infra. 100% in-memory with Map/List, LocalDateTime and Duration.
Reactive dark-mode JavaFX dashboard: metric cards, live line chart, bar chart, sortable activity table, console log, report dialogs.
5 interchangeable RatePolicy implementations hot-swapped at runtime through one unified evaluate() contract.
Clients, services and full request logs survive restarts through data/*.csv with automatic reload on boot.
Everything required to build, run and test the simulator.
| Component | Version | Role in project |
|---|---|---|
| JDK | 17 / 21 | Language runtime, streams, Java Time API |
| JavaFX Controls | 21.0.2 | Dashboard, tables, charts, dialogs |
| JavaFX FXML | 21.0.2 | Declared dependency for view composition |
| Maven | 3.8+ | Build, dependency & javafx-maven-plugin |
| JUnit Jupiter | 5.10.2 | Unit tests for loader / generator / report / chart |
| mainClass | com.smartratelimit_abuse.MainApp | JavaFX Application entry point |
| GroupId | com.runtime_crew | Artifact api-ratelimit-simulator:1.0 |
| Compiler | release 17 · -Xlint:all | Warnings enabled for clean code |
| Scene size | 1000 × 620 | Stage set in MainApp.start() |
<!-- pom.xml core coordinates --> <groupId>com.runtime_crew</groupId> <artifactId>api-ratelimit-simulator</artifactId> <version>1.0</version> <!-- javafx-maven-plugin --> <mainClass>com.smartratelimit_abuse.MainApp</mainClass>
Five layers — Bootstrap → Presentation → Application Service → Policy Strategy → Domain Model → Storage. Fig 3.1 shows responsibility + API of every layer, Fig 3.2 the Strategy UML, Fig 3.3 the package import graph (verified against the source), and the contract table lists what each layer may touch.
com.smartratelimit_abuse.MainApp
1 class
start(Stage) → new EnhancedDashboardView() → scene.show()
Owns the scene graph only: renders state, collects user input and turns clicks into commands. No rate-limit maths lives here — it delegates to services and re-renders what comes back.
The use-case core: admission control, logging, analysis, persistence and report assembly. Pure Java — zero javafx.* imports, so it is unit-testable headlessly.
Pure functions over a time window: no I/O, no JavaFX, no globals. Each algorithm is a separate class, so adding a policy = adding one file and one List.of() entry.
Entities and enums that carry the invariants (isMoreSevereThan, risk score, unmodifiable views). Imports nothing but the JDK — it is the innermost circle.
Human-readable text, one request per line, 18 columns. Survives restart: the dashboard reloads them at boot, so the window state can be rebuilt.
Solid blue = normal dependency · pink = multi-service module · purple dashed = composition-root wiring only · red = forbidden by the dependency rule.
model imports only the JDK, policy imports only model, service imports policy + model, and no service or policy class ever imports javafx.*.| Layer | Package(s) | Classes / LOC | Single responsibility | May depend on | Never touches |
|---|---|---|---|---|---|
| Bootstrap | com.smartratelimit_abuse | 1 / 21 | JavaFX entry point: builds the dashboard, starts the primary stage | ui.dashboard | service, policy, file formats |
| ① Presentation | ui.dashboard multiservice.ui | 17 / 2,909 | Render state, capture input, translate clicks into commands, show reports | service, multiservice.*, model, policy (2 roots only) | CSV parsing, rate maths |
| ② Application Service | service multiservice.service | 14 / 1,556 | Use cases: admission, logging, tracking, analysis, persistence, reports | policy, model, java.io | javafx.* (0 imports verified) |
| ③ Policy Strategy | policy | 6 / 353 | Pure window algorithms: fixed, sliding, burst, anomaly, retry | model, java.time | service, ui, java.io (0 imports verified) |
| ④ Domain Model | model multiservice.model | 8 / 321 | Entities, enums and invariants (monotonic severity, risk score) | JDK only | any project package (0 imports verified) |
| ⑤ Storage | ./data, ./sample_data | 2 dirs / 6 files | Durable 18-column CSV logs, reloaded at boot to rebuild state | written + read via service | direct access from UI classes |
setOnAction, Platform.runLater) which carry data, not types.RatePolicy decouples algorithms from the analyzer; no if/else chains, only polymorphic evaluate().
Domain state private; getters return Collections.unmodifiableList/Map — external mutation impossible.
Heterogeneous policies run through one contract inside RateLimitAnalyzer.analyze().
RequestType, ViolationLevel, EventType each carry display names + descriptions.
JavaFX ≠ business logic ≠ algorithms ≠ models — each isolated in its own package.
AbuseReport.setLevel() never downgrades: NORMAL→WARNING→CRITICAL is a one-way ladder.
Only EnhancedDashboardView (live) and MultiServiceDashboardView (built, never instantiated) construct the 5 policies and inject them into RateLimitAnalyzer — swapping an algorithm is a one-line change.
Import scan of all 46 files: 0 edges from model → project, 0 policy → service, 0 service → ui, and exactly 1 ui → policy (the composition root).
Eight sub-sections: boot stack → 10-stage request graph → admission decision tree → quota lifecycle → 4 ingestion modes → one-click call chain → multi-service fan-out → runtime reconfiguration.
mvn javafx:run (openjfx plugin) starts com.smartratelimit_abuse.MainApp.
start(Stage) news up the root EnhancedDashboardView, then wraps it in Scene(1000×620) + /styles/main.css.
Enforcer 10 req / 3s window / 3s block + Analyzer with 5 policies (thresholds 5, 5, 4, 3, 8).
EnhancedDashboardView ctorDashboardActionHandler, TrafficChartView, MultiServiceSimulator, ServiceSelectorView constructed and injected.
Timeline(KeyFrame 1s → refreshClientViews()), cycle INDEFINITE, play() — drives quota, risk badge and block countdown.
initLayout() (BorderPane), setupEvents() (all setOnAction), then loadFromCsv() restores clients, services and request history.
Title set, scene attached, stage.show(); three start-up lines are appended to the console log.
mvn javafx:run └─ com.smartratelimit_abuse.MainApp.main() └─ launch(args) // starts JavaFX Application Thread └─ start(Stage stage) ├─ new EnhancedDashboardView() │ ├─ enforcer = RateLimitEnforcer(10, 3s, 3s, logger) │ ├─ analyzer = RateLimitAnalyzer(5 policies) │ ├─ actionHandler / trafficChart / multiSimulator │ ├─ statusTimer = Timeline(1s → refreshClientViews) │ ├─ initLayout() & setupEvents() │ └─ loadFromCsv() // clients, services, requests ├─ new Scene(root, 1000, 620) + /styles/main.css └─ stage.show()
Everything — ingestion, policy evaluation, CSV write, UI refresh — runs on the single JavaFX Application Thread. No worker threads: a burst of 20 requests is processed synchronously inside one button click.
While idle, only statusTimer fires every second: recompute remaining quota, getTimeUntilReset(), risk badge and block countdown, then repaint.
loadFromCsv() replays data/requests.csv through restoreFromLogEntries(), so the table, charts and tracker are repopulated before the window is shown.
RateLimitEnforcer.shouldBlock() / processRequest() / getRemainingQuota() (lines 31–106)clearClient() so the quota snaps back to 10/10.handleSendRequest(clientId, type, logPanel, onUpdated) — type is randomized when null, and ALL_CLIENTS replays the same pipeline for every registered client.
generateBurst(clientId, 20, 10s) builds evenly spaced timestamps, then feeds them through processRequest() one by one: the first ~10 pass, the rest trip the cooldown.
DatasetLoader.loadFromFile / loadFromText accepts comma, semicolon or space separators, skips # comments and headers, and stores bad rows in getErrors() instead of aborting.
sendRequest(ids, type) issues one request per service with an isolated quota key svc_<id> and returns a LinkedHashMap in selection order.
// click "Send Request" → DashboardActionHandler.handleSendRequest(...) (lines 62–105) ServiceRequest req = new ServiceRequest(clientId, type, LocalDateTime.now()); // 1 build the event RateLimitEnforcer.RequestResult res = enforcer.processRequest(req); // 2 shouldBlock + maybe logRequest activityTracker.trackRequest(req, res.isBlocked()); // 3 totals, success rate RequestLog log = logger.getLog(clientId); AbuseReport abuse = analyzer.analyze(log); // 4 five policies → level + risk EventType evt = determineEventType(res, abuse); // 5 ALLOWED | RATE_LIMITED | ABUSE_DETECTED String reason = abuse.hasViolations() ? abuse.getSummary() : (res.isBlocked() ? "RATE_LIMIT_EXCEEDED" : "OK"); persistToCsv(req, res, evt, abuse.getRiskScore(), reason); // 6 append to data/requests.csv onUpdated.run(); // 7 table + stats + charts repaint logPanel.appendEvent(evt, clientId, reason + " | Quota: " + res.getRemainingQuota() + "/" + enforcer.getMaxRequests()); // 8 console line
One click is replayed across every selected service and every resolved client. Quotas stay isolated per service because each service is addressed under its own svc_<id> key, while activity stats and CSV rows keep the real client id — two identities for one request, written to a second log file, data/multi_requests.csv.
EnhancedDashboardView:442–514 → MultiServiceSimulator.sendRequest() (:38–71). One action, N independent counters, one CSV.One shared RateLimitEnforcer, N counters: every service is addressed as svc_<id>, so 10 requests to Facebook never spend Messenger’s budget. getRemainingQuota("svc_" + svc) feeds the per-service countdown at :611.
Enforce + analyze under svc_<id>; track + log under actualClient (:39 — blank or svc_-prefixed ids collapse back to WebApp). CSV rows therefore show client and service side by side, and ActivityTableView strips the svc_ prefix when grouping.
handleMultiServiceBurst() :485 → simulateBurst(client, selected, burstCount=20, 10s) lays down a synthetic burst per service and replays it through the same enforcer, returning BurstResult{total, allowed, blocked} (:152) plus a rolled-up total at :509–511.
MultiServiceDashboardView and MultiServiceActionHandler are complete but never instantiated — MainApp opens only EnhancedDashboardView. Their private enforcer (10 / 10s / 3s, MultiServiceDashboardView:40) therefore never runs.
✉️ [WebApp] Sending READ request to 3 service(s)... // :457 ✅ [14:02:11] WebApp -> Facebook | ✅ Allowed | Quota: 7/10 | Risk: 0 | OK ⛔ [14:02:11] WebApp -> Messenger | ⏳ Rate Limited | Quota: 0/10 | Risk: 0 | RATE_LIMIT_EXCEEDED ⛔ [14:02:11] WebApp -> Instagram | ⚠️ Abuse Detected | Quota: 2/10 | Risk: 55 | Burst detected: 5 requests within 3 seconds starting at 14:02:08 ✅ [WebApp] Sent! Allowed: 1 | Blocked: 2 // :480
| blocked | hasViolations() | EventType | meaning |
|---|---|---|---|
| no | no | ✅ ALLOWED | quota left, no policy fired |
| yes | no | ⏳ RATE_LIMITED | 3s cooldown active on that svc_<id> |
| no | yes | ⚠ ABUSE_DETECTED | policy hit while quota still left |
| yes | yes | ⚠ ABUSE_DETECTED | both — abuse always outranks the enforcer |
MultiServiceSimulator.determineEventType() :112–117; byte-for-byte the same logic as DashboardActionHandler:338–343 used by Modes A–C.SettingsView.show() opens a 400×380 scene with max requests, window, block duration, burst count and burst duration; setOnSave(() -> applySettings(...)) binds the button.
applySettings() :516–540 constructs a new RateLimitEnforcer and a new RateLimitAnalyzer (Fixed & Sliding re-thresholded from the dialog), then pushes them with updateEnforcer() / updateAnalyzer() / updateBurstConfig() :532–534.
Success toast, a log line Settings saved: Max=%d, Window=%ds, Block=%ds, Burst=%d/%ds (EnhancedDashboardView:538), refreshClientViews(), and the dialog auto-closes after a 1-second Timeline.
multiSimulator was built at :65 holding the boot enforcer and analyzer. applySettings() only reassigns the view’s fields, so Mode D keeps enforcing 10 requests / 3s window / 3s block with the boot thresholds until the app is restarted.
Five algorithms behind one RatePolicy interface — boot defaults taken straight from EnhancedDashboardView:54–60, the exact trigger math of every detector, and the monotonic severity ladder that feeds the risk score.
Counts requests falling inside a discrete window ending at the latest request timestamp.
> means it trips on the 6th requestPair scan over every continuous interval — kills boundary-burst exploits.
Finds clusters of rapid-fire requests — models DoS spikes and flash mobs.
(4, 3s)HH:mm:ss; multiple overlapping starts multiply the countThree statistical heuristics that unmask automated scripts and bots.
Detects aggressive re-attempts following rejected calls.
One abstract method plus four defaults — every detector drops into the same slot.
policy imports model only — zero awareness of service or uiBefore judging anything it trims the log to the last 10 seconds, then runs every policy over that slice.
DashboardActionHandler:630,666 → analyze(log).getLevel()addViolation() drops duplicates and blank messages, so repeated identical hits never inflate the risk scoregetRiskScore() drives the dashboard risk badge · 0 when there are no violationsisMoreSevereThan() compares ordinal() of NORMAL / WARNING / CRITICAL// EnhancedDashboardView:54–60 → rebuilt identically by applySettings() at :523–530 enforcer = new RateLimitEnforcer(10, ofSeconds(3), ofSeconds(3), logger); // 10 req · 3s window · 3s block analyzer = new RateLimitAnalyzer(List.of( new FixedWindowPolicy(5, ofSeconds(3)), // 5 / 3s → WARNING new SlidingWindowPolicy(5, ofSeconds(3)), // 5 / 3s → CRITICAL new BurstDetectionPolicy(4, ofSeconds(3)), // ≥4 / 3s → WARNING → CRITICAL new AbnormalPatternPolicy(3), // >3 night hits → WARNING new RetryAbusePolicy(8, ofSeconds(2)))); // 8 / 2s → WARNING (sub-second → CRITICAL)
| Request type | Enforcer allows while | Enforcer blocks when | Dominant abuse detector | Consequence |
|---|---|---|---|---|
| READ | requests in window < max | ≥ max | AbnormalPattern — type imbalance > 90% | WARNING scraping / harvesting |
| WRITE | requests in window < max | ≥ max | BurstDetection — ≥4 in 3s | CRITICAL flash flood / DoS |
| UPDATE | requests in window < max | ≥ max | RetryAbuse — ≥8 rapid consecutive | WARNING resource churn |
| DELETE | requests in window < max | ≥ max | AbnormalPattern — uniform interval >70% | CRITICAL bot script |
| ANY | gap ≥ 1000ms between calls | Δ < 1000ms more than 5 times | RetryAbuse — rapid retry loop | CRITICAL lockout |
| ANY | outside 02:00–05:00 | >3 requests at night | AbnormalPattern — off-hours | WARNING nocturnal anomaly |
RateLimitEnforcer (per-client quota, recentCount ≥ max → 3s block). Column 4 is never a router — all five policies always evaluate the same 10-second slice; it names the detector that produces the dominant verdict for that kind of traffic.| Setting (default) | Rebuilt when applied | Stays at boot value forever |
|---|---|---|
| Max Requests (10) · Time Window (3) | Enforcer quota + FixedWindowPolicy + SlidingWindowPolicy (applySettings:523–530) | — |
| Block Duration (3) | Enforcer cooldown only | — |
| Burst Count (20) · Burst Duration (10) | Handler burst fields (DashboardActionHandler:30–31) | — |
| — | — | BurstDetectionPolicy(4, 3s), AbnormalPatternPolicy(3), RetryAbusePolicy(8, 2s) and the analyzer's 10 s slice — no UI control exists for any of them |
multiSimulator is built once with the boot enforcer/analyzer (:65), so the multi-service path keeps 10 req / 3 s / 3 s no matter what the user applies.Every package, every file. Aggregate totals are in the strip below. The same six packages have filter buttons in section 07.
model — Domain7 immutable value objects & enums. The shared vocabulary of the whole system: a request, a client, a log, a report, a severity.
policy — Strategy1 interface + 5 algorithms. Only this package knows how abuse is judged; nothing above knows the details.
service — Business Logic12 engines: admission control, analysis orchestration, activity statistics, CSV ingestion & persistence, ASCII/HTML report rendering.
ui.dashboard — Presentation12 files: 11 JavaFX views/helpers plus DashboardActionHandler. EnhancedDashboardView is the composition root and coordinator.
multiservice — Fan-out8 classes simulating many APIs at once: CSV-backed ServiceRegistry, MultiServiceSimulator, action handler, selector, management dialog, bar chart and dashboard.
test — Verification4 JUnit 5 classes, 442 lines (359 SLOC): BurstTrafficGeneratorTest · DatasetLoaderTest · EnhancedReportGeneratorTest · BarChartViewTest.
Four documents (index.html, README.md, documentation.md, details.md) plus the assignment PDF and pom.xml. The four CSVs under data/ are read at boot and appended to on every click by RequestLogService.
Search or filter all 56 entries by name, responsibility or key API — 46 main classes (one of them the RatePolicy interface, 4 of them carrying 6 nested types) + 4 test classes, every row verified against the source.
| # | Class / Interface | Package | Type | Responsibility | Key API |
|---|
Persistence schemas, sample datasets, domain attribute dictionary and parser rules.
| File | Schema | Purpose |
|---|---|---|
| data/clients.csv | clientId,name | Registered API consumers; 4 defaults auto-seeded (MobileApp, WebApp, PartnerAPI, SuspiciousBot) |
| data/api_services.csv | id,name,isDefault,icon,enabledEventTypes | Facebook / Messenger / Telegram / Instagram + custom services |
| data/requests.csv | requestId, clientId, serviceId, endpoint, method, timestamp, status, responseCode, responseTimeMs, rateLimit, remaining, windowSeconds, abuseDetected, abuseReason, severity, eventType, riskScore, reason | Single-service audit trail (18 columns) |
| data/multi_requests.csv | same 18 columns, serviceId populated | Multi-service fan-out trail |
| sample_data/sample_requests.csv | timestamp,clientId,requestType | Replayable normal / abusive sessions |
| sample_data/test_requests.txt | # comments + rows | Loader robustness test input |
| Attribute | Type | Meaning |
|---|---|---|
| timestamp | LocalDateTime | When the request happened |
| requestType | RequestType enum | READ · WRITE · UPDATE · DELETE |
| clientId | String | Who made the request |
| service | String | Which API service was hit (multi-mode) |
| status | boolean / String | ALLOWED vs BLOCKED |
| severity | ViolationLevel | NORMAL · WARNING · CRITICAL |
| eventType | EventType | REQUEST · ALLOWED · RATE_LIMITED · ABUSE_DETECTED · BLOCKED |
| riskScore | int 0–100 | Computed abuse risk |
// accepted delimiters , ; \s+ // comma | semicolon | whitespace // skipped lines // empty · lines starting with # or // // header containing "timestamp" or "clientId" // accepted timestamp formats yyyy-MM-dd'T'HH:mm:ss // ISO-8601 yyyy-MM-dd HH:mm:ss // date + time HH:mm:ss // time only (today) HH:mm // short time (today) // fault tolerance: bad rows go to errors list loader.getErrors(); loader.getSuccessCount();
How the 12 JavaFX views compose the runtime screen (BorderPane layout, 1000×620).
EnhancedDashboardView = BorderPane(top=TopBar, left=ControlPanel+Stats, center=Table+Chart, bottom=LogPanel)Modal monospaced ASCII security audit with copy-to-clipboard and file export.
Editable max requests · time window · block duration · burst count · burst duration with validation.
Allowed-vs-blocked per service / per type via static aggregateServiceStats().
Command layer: 10+ handle*() methods orchestrate enforcer, tracker, logger, report and CSV.
Aggregates every tenant's records, sorts descending, enables fleet-wide comparison reports.
Background Timeline refreshes remaining quota, risk badge and block countdown every second.
EnhancedReportGenerator — the largest service class, second-largest file in the repo. Six public methods, zero file I/O inside: everything returns a String built with StringBuilder.
API SECURITY REPORT Full Violation & Usage Analysis Generated: 27 Sep 2026 · 22:54:06 [ CRITICAL ] CLIENT INFORMATION ------------------------------------------------------------------ CLIENT ID CLIENT_A REPORT DATE 27 Sep 2026 · 22:54:06 SEVERITY ● CRITICAL ------------------------------------------------------------------ USAGE OVERVIEW TOTAL REQUESTS 80 ALLOWED 8 10.0% BLOCKED 72 90.0% limit exceeded SUCCESS RATE 10.0% █░░░░░░░░░ Last Activity: 19:15:55 REQUEST TYPE DISTRIBUTION READ 80 requests 100.0% ██████████ SECURITY STATUS ------------------------------------------------------------------ 1 VIOLATION(S) DETECTED 1. Sliding window abuse detected: 6 requests within 3s window (limit: 5) ------------------------------------------------------------------ RECOMMENDATION ------------------------------------------------------------------ ✖ CRITICAL Immediate action required! Rate limit threshold breach detected. Consider blocking this client temporarily or rotating API credentials. Low success rate: Check client integration and error handling. ------------------------------------------------------------------
generateViolationReport() — single-client deep audit (68 columns). Snapshot above was produced by actually running the class: 8 allowed + 72 blocked requests fed in, report printed out.| Method | Output |
|---|---|
| generateViolationReport() | Single-client deep audit + recommendation (:16, 68 cols) |
| generateUsageReport() | Single-client quick summary: usage stats, types, timeline, alerts (:123) |
| generateAllClientsUsageReport() | Fleet summary with aligned fixed-width tables + optional service breakdown (:185) |
| generateAllClientsFullReport() | Full 70-column matrix, single & multi-service (:401) |
| generateComparisonReport() | Full HTML document, hard-coded dark hex — dead code, never called (:275) |
| buildServiceComparison() | Returns a Map keyed by service (TreeMap), feeds the UI table — no text output (:383) |
| getProgressBar(n) | private helper: █×filled + ░×empty (:352) |
| Level | Exact advice printed |
|---|---|
| NORMAL | “Usage is within normal operational thresholds. No immediate action is required.” |
| WARNING | “High traffic frequency detected. Monitor client activity. Review request patterns for potential abuse.” |
| CRITICAL | “Immediate action required! Rate limit threshold breach detected. Consider blocking this client temporarily or rotating API credentials.” — plus a low-success-rate line when the rate drops under 50%. |
The only production caller is DashboardActionHandler (7 call sites: :203, 210, 227, 233, 241, 259, 273). ReportViewDialog only renders a String it is handed — it never calls the generator.
Nothing is written automatically and no report/ directory exists. Export happens through a FileChooser (ReportViewDialog:265–279, DashboardActionHandler:260–278) with default names report_*.txt, <client>_report_*.txt, ALL_CLIENTS_report_*.txt.
Severity dot is always \u25CF (●); the icon is ✖ CRITICAL / ⚠ WARNING / ✔ otherwise. Bars are █ filled + ░ empty, width 10 by default.
JUnit Jupiter 5.10.2 (test scope, pom.xml:28–33) · mvn test · 4 classes, 15 tests, 442 lines (359 SLOC) — all green in ~9 s.
| Test Class | @Test | Target | What it actually asserts | Focus areas |
|---|---|---|---|---|
| BurstTrafficGeneratorTest | 1 | BurstTrafficGenerator | generateBurst("CLIENT_A", 20, 10s) returns exactly 20 requests, all stamped CLIENT_A with non-null type & timestamp, and timestamps never run backwards | high-volume synthesis |
| DatasetLoaderTest | 3 | DatasetLoader | Comma-delimited line parses to hour/minute/second + client + type · comment & header lines are skipped (3 ok, 0 errors) · sample_data/test_requests.txt and sample_requests.csv exist and load (run from the Maven basedir) | parsing, fixtures |
| EnhancedReportGeneratorTest | 6 | EnhancedReportGenerator | Full-report section headers · per-service grouping (Facebook 2/1/1 @ 50.0%, Instagram last activity 22:45:09) · blank / "-" services and null input yield empty maps · fixed-width CLIENT/SERVICE tables stay column-aligned, incl. an over-long client name, with 58.6% / 65.5% rate cells | formatting integrity |
| BarChartViewTest | 5 | BarChartView | Static aggregateServiceStats(): per-service allowed/blocked pairs, null/blank/"-" services excluded and trimmed, counters accumulate across calls, null args are safe no-ops · plus an integration run through MultiServiceSimulator + enforcer + analyzer into a temp CSV | null-safety, aggregation |
4 of 46 production classes get direct assertions (8.7%). Zero @BeforeEach, zero assertThrows. Semicolon/space delimiters, malformed-line tolerance and ASCII progress bars exist in production but have no assertions — the table above states only what is proven.
All 5 policy internals beyond construction, the JavaFX dashboard views, the multiservice UI, ClientRegistryService, the CSV parser/handler and MainApp — 33 of 46 classes are exercised only by running the app itself.
[INFO] Running com.runtime_crew.api_simulator.service.BurstTrafficGeneratorTest [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] Running com.runtime_crew.api_simulator.service.DatasetLoaderTest [INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0 [INFO] Running com.runtime_crew.api_simulator.service.EnhancedReportGeneratorTest [INFO] Tests run: 6, Failures: 0, Errors: 0, Skipped: 0 [INFO] Running com.runtime_crew.api_simulator.ui.dashboard.BarChartViewTest [INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0 [INFO] Results: Tests run: 15, Failures: 0, Errors: 0, Skipped: 0 [INFO] BUILD SUCCESS — Total time: 8.976 s (verified 2026-09-27)
From clone to running app in five commands. Coordinates com.runtime_crew:api-ratelimit-simulator:1.0 — Maven-only (no mvnw, no shell/batch scripts, no Makefile), verified here with Maven 3.9.16.
pom.xml (61 lines) actually declaresJava release 17 · 3 deps: javafx-controls + javafx-fxml (21.0.2, compile) and junit-jupiter (5.10.2, test) · 2 plugins: maven-compiler-plugin 3.15.0 (-Xlint:all, showWarnings) and javafx-maven-plugin 0.0.8 with mainClass=com.smartratelimit_abuse.MainApp. No repositories, no profiles, no shade/assembly plugin.
mvn clean package writes target/api-ratelimit-simulator-1.0.jar — a plain jar with no Main-Class manifest, so java -jar will not launch the app. The supported run path is mvn javafx:run (the goal comes from the CLI; the POM only pins the plugin version).
data/*.csv (requests, multi-requests, clients, services) are gitignored and auto-created on first run; sample_data/ must exist because DatasetLoaderTest reads it from the Maven basedir.
| Requirement | Minimum | Recommended |
|---|---|---|
| OS | Windows 10 / macOS 12 / Ubuntu 20.04 | Windows 11 / Sonoma / 22.04 LTS |
| JDK | OpenJDK 17 | OpenJDK 21 LTS (Temurin) |
| Maven | 3.8.0 | 3.9.6 |
| Display | 1024 × 768 | 1920 × 1080 |
Pick MobileApp, WebApp, PartnerAPI, SuspiciousBot or register a new tenant.
Fixed Window · Sliding Window · Burst Detection · Abnormal Pattern — swapped through the Strategy interface.
Manual request, 20-request burst, or load sample_requests.csv to replay a recorded session.
Watch live counters, filter the activity table, then open the Abuse Report for block ratios & advice.
| File | Contents |
|---|---|
| README.md | Features, quick start, team table, architecture ASCII diagram |
| documentation.md | 752-line full technical specification (11 chapters) |
| details.md | Assignment brief, concept overview, required features |
| index.html | Project website — architecture, class index, policies, reports, tests and timeline of the simulator |
| documentation/answer.html | Assignment answer sheet — objective, UML diagram, worked trace, sample report, OOP mapping |
| project_file.pdf | Submitted project report |
Two standard diagrams for the project report / viva: domain class relationships and a full request sequence.
DashboardActionHandler.handleSendRequest()Every assignment requirement from details.md mapped to the exact implementing class.
| ✓ | Required feature / concept | Source | Implemented by | Status |
|---|---|---|---|---|
| ✓ | Register API clients | Feature must implement | ClientRegistryService + ControlPanelView | DONE |
| ✓ | Record simulated API requests | Feature must implement | ServiceRequest + RequestLogger + RequestLogService | DONE |
| ✓ | Apply rate-limit policies | Feature must implement | RatePolicy (5 implementations) | DONE |
| ✓ | Detect excessive requests within time windows | Feature must implement | RateLimitEnforcer + SlidingWindowPolicy | DONE |
| ✓ | Generate per-client abuse report | Feature must implement | EnhancedReportGenerator + AbuseReport | DONE |
| ✓ | Domain modelling (Client, RequestLog, RatePolicy) | Core OOP | model/* + policy/RatePolicy | DONE |
| ✓ | Encapsulation of counters and thresholds | Core OOP | private fields + unmodifiable views | DONE |
| ✓ | Polymorphism across policies | Core OOP | RateLimitAnalyzer.analyze() loop | DONE |
| ✓ | Strategy-like design for evaluation | Core OOP | «interface» RatePolicy | DONE |
| ✓ | Use of Enums (RequestType, ViolationLevel) | Core OOP | RequestType, ViolationLevel, EventType | DONE |
| ✓ | Data option A: manual entry via JavaFX form | Data generation | ControlPanelView + handleSendRequest() | DONE |
| ✓ | Data option B: predefined .txt / .csv dataset | Data generation | DatasetLoader + sample_data/* | DONE |
| ✓ | Data option C: burst generator button | Data generation | BurstTrafficGenerator | DONE |
| ✓ | JavaFX GUI | Tools | 17 UI classes (views, dialogs, handlers) + main.css | DONE |
| ✓ | Java Collections (Map, List) | Tools | HashMap / LinkedHashMap / ArrayList throughout | DONE |
| ✓ | Java Time API (LocalDateTime, Duration) | Tools | ServiceRequest, Enforcer, all policies | DONE |
| ✓ | File I/O import / export of request logs | Optional tools | CsvFileHandler + ReportViewDialog export | DONE |
| ✓ | Suggested class: Client (clientId, name) | Suggested structure | model/Client | DONE |
| ✓ | Suggested class: RequestLog | Suggested structure | model/RequestLog | DONE |
| ✓ | Suggested class: RatePolicy interface | Suggested structure | policy/RatePolicy | DONE |
| ✓ | Suggested class: FixedWindowPolicy | Suggested structure | policy/FixedWindowPolicy | DONE |
| ✓ | Suggested class: SlidingWindowPolicy | Suggested structure | policy/SlidingWindowPolicy | DONE |
| ✓ | Suggested class: AbuseReport (violations, severity) | Suggested structure | model/AbuseReport | DONE |
| ✓ | No networking / HTTP / real servers | Out of scope | zero socket usage in codebase | RESPECTED |
| ✓ | Bonus: multi-service fan-out simulation | Beyond brief | multiservice/* (8 classes) | BONUS |
| ✓ | Bonus: charts, settings, persistence, tests | Beyond brief | TrafficChartView, SettingsView, CSV, JUnit | BONUS |
14 commits from first skeleton to final submission — told through the files each one touched.
pom.xml, README.md and MainApp under com.async_alpha: 6 model classes, FixedWindowPolicy + SlidingWindowPolicy + the RatePolicy contract, a compact analyzer, a plain ReportGenerator, RequestLogger, one DashboardView and the base stylesheet.ui/DashboardView is replaced by a root-level view; details.md appears; ClientActivityTracker and RateLimitEnforcer land — tracking and admission control become real services.BurstDetectionPolicy, RetryAbusePolicy, AbnormalPatternPolicy — plus an EnhancedReportGenerator and a flat EnhancedDashboardView; DashboardView moves into ui/.ui.dashboard classes (table, control panel, action handler, log panel, report dialog …); DatasetLoader and BurstTrafficGenerator join, MainApp moves to com.smartratelimit_abuse, sample_data/ and the first two test classes appear.documentation.md lands (752 lines, 11 chapters); the OVERALL STATS column alignment is fixed (+218/−21 in the generator), ReportViewDialog grows by 164 lines and the first report-formatting test is written.async_alpha → runtime_crew across every source and test file; the legacy RatePolicy is replaced by a new contract and the old ReportGenerator is deleted.TrafficChartView brings the live 60-second line chart; the enforcer is reworked (+49/−2) to feed it and the stylesheet gains 91 lines.SettingsView exposes max / window / block / burst tunables and BarChartView adds per-service bars; the dashboard is rewired to rebuild on apply.ClientRegistryService, CsvFileHandler, CsvParser, CsvRequestLogEntry, RequestLogService — logs survive restarts and the first data/*.csv files are committed..gitignore gains data/*.csv (runtime data leaves version control) and the whole multiservice package lands — registry, simulator and 5 UI classes including the ServiceManagementDialog — plus the EventType enum.BarChartView reworked (+102/−19), and the suite reaches its final shape: EnhancedReportGeneratorTest grows to 6 tests and BarChartViewTest adds 5.Cost of every policy, the enforcer and the analyzer slice, plus the tunables that control them. Notation: N = entries in a client's whole log · n = entries handed to the policy (the analyzer's 10 s slice) · c = clients · R = activity records on screen.
| Component | Time | Extra memory | Boot threshold | Window it reads | Escalates to | Best for |
|---|---|---|---|---|---|---|
| FixedWindowPolicy | O(n) | O(1) | count > 5 | 3 s (boot) | WARNING | Simple quota metering, cheapest option |
| SlidingWindowPolicy | O(n²) worst case early return on first hit | O(1) | count > 5 | 3 s (boot) | CRITICAL | Strict limits, boundary-burst protection |
| BurstDetectionPolicy | O(n·k) typical O(n²) if no gap > window (the break never fires) | O(1) | ≥ 4 req / 3 s | 3 s (boot) | WARNING → CRITICAL at 3 bursts | DoS spikes, flash crowds |
| AbnormalPatternPolicy | 3 × O(n) | O(n) intervals[] + type map | >3 night req · >90% one type (≥10) · >70% uniform (≥10) | the 10 s slice no window of its own | WARNING / CRITICAL | Scrapers, bots, off-hours abuse |
| RetryAbusePolicy | 2 × O(n) | O(1) | >5 @ <1000 ms · ≥8 @ ≤2 s | adjacent pairs only | WARNING / CRITICAL | Aggressive retries after rejection |
| RateLimitEnforcer | O(N) per check stream scans the whole client log | O(c) + O(Ntotal) blockStartTimes + RequestLogger | max = 10 requests | 3 s | 3 s block cooldown | Real-time admission control |
| RateLimitAnalyzer (slicer) | O(N) filter + O(n) copy | O(n) | runs all 5 policies each time | 10 s hardcoded RateLimitAnalyzer:23 | feeds AbuseReport | Narrows the log before detection |
EnhancedDashboardView:56–58) while the analyzer first trims every log to its last 10 s (RateLimitAnalyzer:23 — an inline literal, no named constant anywhere in the repo).| Setting | Default | Effect |
|---|---|---|
| Max Requests | 10 | Requests allowed per window before blocking |
| Time Window (sec) | 3 | Window length used by the enforcer |
| Block Duration (sec) | 3 | Cool-down after quota is exhausted |
| Burst Request Count | 20 | Requests synthesized per burst run |
| Burst Duration (sec) | 10 | Time span the burst is spread across |
SettingsView:21–25; applying them rebuilds the enforcer & first two policies (EnhancedDashboardView:516–540).| Level | Meaning | Risk score |
|---|---|---|
| NORMAL | Usage within acceptable limits | 0 (empty list) / violations×15 |
| WARNING | Unusual patterns — monitor the client | violations×15 + 20 |
| CRITICAL | Severe abuse — block / rotate credentials | violations×15 + 40 |
getRiskScore() = min(violations×15 + {20 | 40}, 100) — the 100 cap applies to every non-empty report; isMoreSevereThan() compares enum ordinals and setLevel() blocks downgrades.getRemainingQuota O(N) + analyzer.analyze O(N + n + policies) + table copy/filter O(Rc) + chart re-bucket O(Rc) — lines EnhancedDashboardView:654–673.
Two per-client loops (quota at :596–599, analyzer at :627–641) re-run the sliding pair scan for every client every second, then setAllRecords sorts R records — ActivityTableView:149.
Status timer Timeline(1s) at EnhancedDashboardView:68; chart timer 1 s over a 60 s window in 3 s buckets (20 buckets, TrafficChartView:30–31,103). No disk I/O in the tick — loadAll() is in-memory.
RequestLogService.append does one open/append/close per request (CsvFileHandler:38–42, O(1) CPU + I/O) with a HashSet of seen ids for de-duplication.
One-line definitions for every important concept used across the project.
| Term | Definition | Where it lives |
|---|---|---|
| Rate Limiting | Capping how many requests a client may issue inside a time window | RateLimitEnforcer |
| Sliding Window | A rolling interval measured from each request forward, so window boundaries cannot be gamed | SlidingWindowPolicy |
| Fixed Window | A discrete bucket of time; simple but vulnerable to bursts straddling the boundary | FixedWindowPolicy |
| Quota | Remaining requests a client may still send before being blocked | getRemainingQuota() |
| Block Cooldown | Lockout period after the quota is exhausted; no requests admitted during it | blockDuration field |
| Burst | A dense cluster of requests inside a very short span (default ≥4 in 3s) | BurstDetectionPolicy |
| Abuse / Anomaly | Behaviour statistically unlike a legitimate client (off-hours, uniform rhythm, one-sided types) | AbnormalPatternPolicy |
| Violation Level | Severity state NORMAL / WARNING / CRITICAL assigned to a client | ViolationLevel enum |
| Risk Score | 0–100 numeric risk derived from violation count and level | AbuseReport.getRiskScore() |
| Strategy Pattern | Encapsulating interchangeable algorithms behind one interface | RatePolicy |
| Admission Control | The gate that decides ALLOWED vs BLOCKED for every incoming request | processRequest() |
| Event Type | Classified outcome of a request: REQUEST, ALLOWED, RATE_LIMITED, ABUSE_DETECTED, BLOCKED | EventType enum |
| Multi-Service Mode | Issuing one action across N independent API services, each with its own quota | MultiServiceSimulator |
| Success Rate | allowed × 100 / total requests for a client | ClientActivity.getSuccessRate() |
| All Clients Mode | Aggregated view merging every tenant's records into one dashboard | EnhancedDashboardView |
| Dataset Import | Batch replay of a CSV/TXT traffic log through the same pipeline | DatasetLoader |
| Analyzer 10-second slice | Every log is first trimmed to its last 10 s (hardcoded literal) before the policies run — the 3 s policy windows sit inside it | RateLimitAnalyzer:23 |
| Severity monotonicity | A report can only escalate NORMAL→WARNING→CRITICAL, never downgrade; repeated identical violations are stored once | AbuseReport.setLevel() / addViolation() |
| Composition Root | The single constructor that wires every engine together — the only place boot defaults (10 / 3 s / 3 s) exist | EnhancedDashboardView:54–60 |
| CSV persistence | Append-on-request logging with open/append/close per row and a seen-id set; reloaded at boot so history survives restarts | RequestLogService / CsvFileHandler |
Questions that come up in demos, viva and first-time setup.
No. There are zero sockets, HTTP clients or servers. Every request is a Java object processed in memory with collections and the Java Time API.
The enforcer admits at most maxRequests (10 at boot) inside its 3-second window — once the recent count reaches the max, shouldBlock() returns true and starts the 3-second cooldown. The five policies judge separately at their own thresholds (5 per 3 s for Fixed/Sliding) and only raise severity, they never admit or block.
Severity is monotonic by design. AbuseReport.setLevel() refuses to move from CRITICAL to WARNING or NORMAL, so a later benign check cannot erase an earlier critical finding.
Sliding Window for the strict-limit story, Burst Detection for the DoS story, Abnormal Pattern for the bot/scraping story. Use a burst of 20 requests to show ALLOWED→BLOCKED within seconds.
Runtime CSVs under data/ are intentionally untracked since the 2026-09-20 timeline entry (.gitignore → data/*.csv). They are re-created with defaults on the next launch.
Confirm java -version is 17+, mvn -v is 3.8+, and you are in the project root (where pom.xml lives). Then mvn clean test before running.
Logs reload from data/requests.csv and data/multi_requests.csv. Delete those files if you want a clean slate, then restart.
Yes — open Settings in the control panel to change max requests, window, block duration and burst parameters, then save. The enforcer and generator are rebuilt from those values.
Each service is keyed as svc_<serviceId> inside the enforcer, so Facebook and Instagram never share a counter even when one client calls both.
Use the ▶ Presentation button for the 14-slide deck (arrow keys, Esc to exit), or read the Documentation Map above and jump chapter by chapter.