diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index 1e37de2..e012797 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -8,31 +8,11 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index a2463ef..1f134f4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -83,6 +83,17 @@
pom
import
+
+
+ org.opentest4j
+ opentest4j
+ 1.3.0
+
+
+ org.slf4j
+ slf4j-api
+ 2.0.9
+
@@ -305,7 +316,7 @@
-Xdiags:verbose
-Xlint:all
- -parameters
+ -proc:none
true
diff --git a/src/main/java/auctiora/AuctionMonitorHealthCheck.java b/src/main/java/auctiora/AuctionMonitorHealthCheck.java
index f79cbe4..cd69e70 100644
--- a/src/main/java/auctiora/AuctionMonitorHealthCheck.java
+++ b/src/main/java/auctiora/AuctionMonitorHealthCheck.java
@@ -6,6 +6,7 @@ import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Liveness;
import org.eclipse.microprofile.health.Readiness;
+import org.eclipse.microprofile.health.Startup;
import java.nio.file.Files;
import java.nio.file.Paths;
@@ -73,7 +74,7 @@ public class AuctionMonitorHealthCheck {
* Startup probe - checks if application has started correctly
* GET /health/started
*/
- @org.eclipse.microprofile.health.Startup
+ @Startup
@ApplicationScoped
public static class StartupCheck implements HealthCheck {
diff --git a/src/main/java/auctiora/AuctionMonitorProducer.java b/src/main/java/auctiora/AuctionMonitorProducer.java
index cad8a80..a970e0c 100644
--- a/src/main/java/auctiora/AuctionMonitorProducer.java
+++ b/src/main/java/auctiora/AuctionMonitorProducer.java
@@ -54,7 +54,7 @@ public class AuctionMonitorProducer {
public ImageProcessingService produceImageProcessingService(
DatabaseService db,
ObjectDetectionService detector,
- RateLimitedHttpClient2 httpClient) {
+ RateLimitedHttpClient httpClient) {
LOG.infof("Initializing ImageProcessingService");
return new ImageProcessingService(db, detector, httpClient);
diff --git a/src/main/java/auctiora/AuctionMonitorResource.java b/src/main/java/auctiora/AuctionMonitorResource.java
index 96c5f52..06c98da 100644
--- a/src/main/java/auctiora/AuctionMonitorResource.java
+++ b/src/main/java/auctiora/AuctionMonitorResource.java
@@ -33,7 +33,7 @@ public class AuctionMonitorResource {
NotificationService notifier;
@Inject
- RateLimitedHttpClient2 httpClient;
+ RateLimitedHttpClient httpClient;
/**
* GET /api/monitor/status
diff --git a/src/main/java/auctiora/ImageProcessingService.java b/src/main/java/auctiora/ImageProcessingService.java
index dd36e7d..9053535 100644
--- a/src/main/java/auctiora/ImageProcessingService.java
+++ b/src/main/java/auctiora/ImageProcessingService.java
@@ -1,7 +1,6 @@
package auctiora;
import lombok.extern.slf4j.Slf4j;
-import java.io.Console;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
@@ -18,11 +17,11 @@ import java.util.List;
@Slf4j
class ImageProcessingService {
- private final RateLimitedHttpClient2 httpClient;
- private final DatabaseService db;
+ private final RateLimitedHttpClient httpClient;
+ private final DatabaseService db;
private final ObjectDetectionService detector;
- ImageProcessingService(DatabaseService db, ObjectDetectionService detector, RateLimitedHttpClient2 httpClient) {
+ ImageProcessingService(DatabaseService db, ObjectDetectionService detector, RateLimitedHttpClient httpClient) {
this.httpClient = httpClient;
this.db = db;
this.detector = detector;
@@ -64,6 +63,8 @@ class ImageProcessingService {
if (e instanceof InterruptedException) {
Thread.currentThread().interrupt();
}
+ } catch (Exception e) {
+ throw new RuntimeException(e);
}
return null;
}
diff --git a/src/main/java/auctiora/Main.java b/src/main/java/auctiora/Main.java
index e3dccef..ad57608 100644
--- a/src/main/java/auctiora/Main.java
+++ b/src/main/java/auctiora/Main.java
@@ -23,6 +23,12 @@ import org.opencv.core.Core;
@Slf4j
public class Main {
+ @SuppressWarnings("restricted")
+ private static Object loadOpenCV() {
+ System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
+ return null;
+ }
+
public static void main(String[] args) throws Exception {
log.info("=== Troostwijk Auction Monitor ===\n");
@@ -40,7 +46,7 @@ public class Main {
// Load native OpenCV library (only if models exist)
try {
- System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
+ loadOpenCV();
log.info("✓ OpenCV loaded");
} catch (UnsatisfiedLinkError e) {
log.info("⚠️ OpenCV not available - image detection disabled");
diff --git a/src/main/java/auctiora/RateLimitedHttpClient.java b/src/main/java/auctiora/RateLimitedHttpClient.java
index c314e90..8aebcee 100644
--- a/src/main/java/auctiora/RateLimitedHttpClient.java
+++ b/src/main/java/auctiora/RateLimitedHttpClient.java
@@ -2,7 +2,9 @@ package auctiora;
import jakarta.enterprise.context.ApplicationScoped;
import org.eclipse.microprofile.config.inject.ConfigProperty;
+import org.jboss.logging.Logger;
+import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
@@ -10,66 +12,259 @@ import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-import io.github.bucket4j.*;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+/**
+ * Rate-limited HTTP client that enforces per-host request limits.
+ *
+ * Features:
+ * - Per-host rate limiting (configurable max requests per second)
+ * - Request counting and monitoring
+ * - Thread-safe using semaphores
+ * - Automatic host extraction from URLs
+ *
+ * This prevents overloading external services like Troostwijk and getting blocked.
+ */
@ApplicationScoped
public class RateLimitedHttpClient {
- private final HttpClient client = HttpClient.newBuilder()
- .connectTimeout(Duration.ofSeconds(30))
- .build();
+ private static final Logger LOG = Logger.getLogger(RateLimitedHttpClient.class);
+
+ private final HttpClient httpClient;
+ private final Map rateLimiters;
+ private final Map requestStats;
@ConfigProperty(name = "auction.http.rate-limit.default-max-rps", defaultValue = "2")
- int defaultRps;
+ int defaultMaxRequestsPerSecond;
@ConfigProperty(name = "auction.http.rate-limit.troostwijk-max-rps", defaultValue = "1")
- int troostwijkRps;
+ int troostwijkMaxRequestsPerSecond;
@ConfigProperty(name = "auction.http.timeout-seconds", defaultValue = "30")
int timeoutSeconds;
- private final Map buckets = new ConcurrentHashMap<>();
+ public RateLimitedHttpClient() {
+ this.httpClient = HttpClient.newBuilder()
+ .connectTimeout(Duration.ofSeconds(30))
+ .build();
+ this.rateLimiters = new ConcurrentHashMap<>();
+ this.requestStats = new ConcurrentHashMap<>();
+ }
- private Bucket bucketForHost(String host) {
- return buckets.computeIfAbsent(host, h -> {
- int rps = host.contains("troostwijk") ? troostwijkRps : defaultRps;
- var limit = Bandwidth.simple(rps, Duration.ofSeconds(1));
- return Bucket4j.builder().addLimit(limit).build();
+ /**
+ * Sends a GET request with automatic rate limiting based on host.
+ */
+ public HttpResponse sendGet(String url) throws IOException, InterruptedException {
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create(url))
+ .timeout(Duration.ofSeconds(timeoutSeconds))
+ .GET()
+ .build();
+
+ return send(request, HttpResponse.BodyHandlers.ofString());
+ }
+
+ /**
+ * Sends a request for binary data (like images) with rate limiting.
+ */
+ public HttpResponse sendGetBytes(String url) throws IOException, InterruptedException {
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create(url))
+ .timeout(Duration.ofSeconds(timeoutSeconds))
+ .GET()
+ .build();
+
+ return send(request, HttpResponse.BodyHandlers.ofByteArray());
+ }
+
+ /**
+ * Sends any HTTP request with automatic rate limiting.
+ */
+ public HttpResponse send(HttpRequest request, HttpResponse.BodyHandler bodyHandler)
+ throws IOException, InterruptedException {
+
+ String host = extractHost(request.uri());
+ RateLimiter limiter = getRateLimiter(host);
+ RequestStats stats = getRequestStats(host);
+
+ // Enforce rate limit (blocks if necessary)
+ limiter.acquire();
+
+ // Track request
+ stats.incrementTotal();
+ long startTime = System.currentTimeMillis();
+
+ try {
+ HttpResponse response = httpClient.send(request, bodyHandler);
+
+ long duration = System.currentTimeMillis() - startTime;
+ stats.recordSuccess(duration);
+
+ LOG.debugf("HTTP %d %s %s (%dms)",
+ response.statusCode(), request.method(), host, duration);
+
+ // Track rate limit violations (429 = Too Many Requests)
+ if (response.statusCode() == 429) {
+ stats.incrementRateLimited();
+ LOG.warnf("⚠️ Rate limited by %s (HTTP 429)", host);
+ }
+
+ return response;
+
+ } catch (IOException | InterruptedException e) {
+ stats.incrementFailed();
+ LOG.warnf("❌ HTTP request failed for %s: %s", host, e.getMessage());
+ throw e;
+ }
+ }
+
+ /**
+ * Gets or creates a rate limiter for a specific host.
+ */
+ private RateLimiter getRateLimiter(String host) {
+ return rateLimiters.computeIfAbsent(host, h -> {
+ int maxRps = getMaxRequestsPerSecond(h);
+ LOG.infof("Initializing rate limiter for %s: %d req/s", h, maxRps);
+ return new RateLimiter(maxRps);
});
}
- public HttpResponse sendGet(String url) throws Exception {
- var req = HttpRequest.newBuilder()
- .uri(URI.create(url))
- .timeout(Duration.ofSeconds(timeoutSeconds))
- .GET()
- .build();
- return send(req, HttpResponse.BodyHandlers.ofString());
+ /**
+ * Gets or creates request stats for a specific host.
+ */
+ private RequestStats getRequestStats(String host) {
+ return requestStats.computeIfAbsent(host, h -> new RequestStats(h));
}
- public HttpResponse sendGetBytes(String url) throws Exception {
- var req = HttpRequest.newBuilder()
- .uri(URI.create(url))
- .timeout(Duration.ofSeconds(timeoutSeconds))
- .GET()
- .build();
- return send(req, HttpResponse.BodyHandlers.ofByteArray());
+ /**
+ * Determines max requests per second for a given host.
+ */
+ private int getMaxRequestsPerSecond(String host) {
+ if (host.contains("troostwijk")) {
+ return troostwijkMaxRequestsPerSecond;
+ }
+ return defaultMaxRequestsPerSecond;
}
- public HttpResponse send(HttpRequest req,
- HttpResponse.BodyHandler handler) throws Exception {
- String host = req.uri().getHost();
- var bucket = bucketForHost(host);
- bucket.asBlocking().consume(1);
+ /**
+ * Extracts host from URI (e.g., "api.troostwijkauctions.com").
+ */
+ private String extractHost(URI uri) {
+ return uri.getHost() != null ? uri.getHost() : uri.toString();
+ }
- var start = System.currentTimeMillis();
- var resp = client.send(req, handler);
- var duration = System.currentTimeMillis() - start;
+ /**
+ * Gets statistics for all hosts.
+ */
+ public Map getAllStats() {
+ return Map.copyOf(requestStats);
+ }
- // (Optional) Logging
- System.out.printf("HTTP %d %s %s in %d ms%n",
- resp.statusCode(), req.method(), host, duration);
+ /**
+ * Gets statistics for a specific host.
+ */
+ public RequestStats getStats(String host) {
+ return requestStats.get(host);
+ }
- return resp;
+ /**
+ * Rate limiter implementation using token bucket algorithm.
+ * Allows burst traffic up to maxRequestsPerSecond, then enforces steady rate.
+ */
+ private static class RateLimiter {
+ private final Semaphore semaphore;
+ private final int maxRequestsPerSecond;
+ private final long intervalNanos;
+
+ RateLimiter(int maxRequestsPerSecond) {
+ this.maxRequestsPerSecond = maxRequestsPerSecond;
+ this.intervalNanos = TimeUnit.SECONDS.toNanos(1) / maxRequestsPerSecond;
+ this.semaphore = new Semaphore(maxRequestsPerSecond);
+
+ // Refill tokens periodically
+ startRefillThread();
+ }
+
+ void acquire() throws InterruptedException {
+ semaphore.acquire();
+
+ // Enforce minimum delay between requests
+ long delayMillis = intervalNanos / 1_000_000;
+ if (delayMillis > 0) {
+ Thread.sleep(delayMillis);
+ }
+ }
+
+ private void startRefillThread() {
+ Thread refillThread = new Thread(() -> {
+ while (!Thread.currentThread().isInterrupted()) {
+ try {
+ Thread.sleep(1000); // Refill every second
+ int toRelease = maxRequestsPerSecond - semaphore.availablePermits();
+ if (toRelease > 0) {
+ semaphore.release(toRelease);
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ break;
+ }
+ }
+ }, "RateLimiter-Refill");
+ refillThread.setDaemon(true);
+ refillThread.start();
+ }
+ }
+
+ /**
+ * Statistics tracker for HTTP requests per host.
+ */
+ public static class RequestStats {
+ private final String host;
+ private final AtomicLong totalRequests = new AtomicLong(0);
+ private final AtomicLong successfulRequests = new AtomicLong(0);
+ private final AtomicLong failedRequests = new AtomicLong(0);
+ private final AtomicLong rateLimitedRequests = new AtomicLong(0);
+ private final AtomicLong totalDurationMs = new AtomicLong(0);
+
+ RequestStats(String host) {
+ this.host = host;
+ }
+
+ void incrementTotal() {
+ totalRequests.incrementAndGet();
+ }
+
+ void recordSuccess(long durationMs) {
+ successfulRequests.incrementAndGet();
+ totalDurationMs.addAndGet(durationMs);
+ }
+
+ void incrementFailed() {
+ failedRequests.incrementAndGet();
+ }
+
+ void incrementRateLimited() {
+ rateLimitedRequests.incrementAndGet();
+ }
+
+ // Getters
+ public String getHost() { return host; }
+ public long getTotalRequests() { return totalRequests.get(); }
+ public long getSuccessfulRequests() { return successfulRequests.get(); }
+ public long getFailedRequests() { return failedRequests.get(); }
+ public long getRateLimitedRequests() { return rateLimitedRequests.get(); }
+ public long getAverageDurationMs() {
+ long successful = successfulRequests.get();
+ return successful > 0 ? totalDurationMs.get() / successful : 0;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s: %d total, %d success, %d failed, %d rate-limited, avg %dms",
+ host, getTotalRequests(), getSuccessfulRequests(),
+ getFailedRequests(), getRateLimitedRequests(), getAverageDurationMs());
+ }
}
}
diff --git a/src/main/java/auctiora/RateLimitedHttpClient2.java b/src/main/java/auctiora/RateLimitedHttpClient2.java
deleted file mode 100644
index b963bd7..0000000
--- a/src/main/java/auctiora/RateLimitedHttpClient2.java
+++ /dev/null
@@ -1,270 +0,0 @@
-package auctiora;
-
-import jakarta.enterprise.context.ApplicationScoped;
-import org.eclipse.microprofile.config.inject.ConfigProperty;
-import org.jboss.logging.Logger;
-
-import java.io.IOException;
-import java.net.URI;
-import java.net.http.HttpClient;
-import java.net.http.HttpRequest;
-import java.net.http.HttpResponse;
-import java.time.Duration;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicLong;
-
-/**
- * Rate-limited HTTP client that enforces per-host request limits.
- *
- * Features:
- * - Per-host rate limiting (configurable max requests per second)
- * - Request counting and monitoring
- * - Thread-safe using semaphores
- * - Automatic host extraction from URLs
- *
- * This prevents overloading external services like Troostwijk and getting blocked.
- */
-@ApplicationScoped
-public class RateLimitedHttpClient2 {
-
- private static final Logger LOG = Logger.getLogger(RateLimitedHttpClient2.class);
-
- private final HttpClient httpClient;
- private final Map rateLimiters;
- private final Map requestStats;
-
- @ConfigProperty(name = "auction.http.rate-limit.default-max-rps", defaultValue = "2")
- int defaultMaxRequestsPerSecond;
-
- @ConfigProperty(name = "auction.http.rate-limit.troostwijk-max-rps", defaultValue = "1")
- int troostwijkMaxRequestsPerSecond;
-
- @ConfigProperty(name = "auction.http.timeout-seconds", defaultValue = "30")
- int timeoutSeconds;
-
- public RateLimitedHttpClient2() {
- this.httpClient = HttpClient.newBuilder()
- .connectTimeout(Duration.ofSeconds(30))
- .build();
- this.rateLimiters = new ConcurrentHashMap<>();
- this.requestStats = new ConcurrentHashMap<>();
- }
-
- /**
- * Sends a GET request with automatic rate limiting based on host.
- */
- public HttpResponse sendGet(String url) throws IOException, InterruptedException {
- HttpRequest request = HttpRequest.newBuilder()
- .uri(URI.create(url))
- .timeout(Duration.ofSeconds(timeoutSeconds))
- .GET()
- .build();
-
- return send(request, HttpResponse.BodyHandlers.ofString());
- }
-
- /**
- * Sends a request for binary data (like images) with rate limiting.
- */
- public HttpResponse sendGetBytes(String url) throws IOException, InterruptedException {
- HttpRequest request = HttpRequest.newBuilder()
- .uri(URI.create(url))
- .timeout(Duration.ofSeconds(timeoutSeconds))
- .GET()
- .build();
-
- return send(request, HttpResponse.BodyHandlers.ofByteArray());
- }
-
- /**
- * Sends any HTTP request with automatic rate limiting.
- */
- public HttpResponse send(HttpRequest request, HttpResponse.BodyHandler bodyHandler)
- throws IOException, InterruptedException {
-
- String host = extractHost(request.uri());
- RateLimiter limiter = getRateLimiter(host);
- RequestStats stats = getRequestStats(host);
-
- // Enforce rate limit (blocks if necessary)
- limiter.acquire();
-
- // Track request
- stats.incrementTotal();
- long startTime = System.currentTimeMillis();
-
- try {
- HttpResponse response = httpClient.send(request, bodyHandler);
-
- long duration = System.currentTimeMillis() - startTime;
- stats.recordSuccess(duration);
-
- LOG.debugf("HTTP %d %s %s (%dms)",
- response.statusCode(), request.method(), host, duration);
-
- // Track rate limit violations (429 = Too Many Requests)
- if (response.statusCode() == 429) {
- stats.incrementRateLimited();
- LOG.warnf("⚠️ Rate limited by %s (HTTP 429)", host);
- }
-
- return response;
-
- } catch (IOException | InterruptedException e) {
- stats.incrementFailed();
- LOG.warnf("❌ HTTP request failed for %s: %s", host, e.getMessage());
- throw e;
- }
- }
-
- /**
- * Gets or creates a rate limiter for a specific host.
- */
- private RateLimiter getRateLimiter(String host) {
- return rateLimiters.computeIfAbsent(host, h -> {
- int maxRps = getMaxRequestsPerSecond(h);
- LOG.infof("Initializing rate limiter for %s: %d req/s", h, maxRps);
- return new RateLimiter(maxRps);
- });
- }
-
- /**
- * Gets or creates request stats for a specific host.
- */
- private RequestStats getRequestStats(String host) {
- return requestStats.computeIfAbsent(host, h -> new RequestStats(h));
- }
-
- /**
- * Determines max requests per second for a given host.
- */
- private int getMaxRequestsPerSecond(String host) {
- if (host.contains("troostwijk")) {
- return troostwijkMaxRequestsPerSecond;
- }
- return defaultMaxRequestsPerSecond;
- }
-
- /**
- * Extracts host from URI (e.g., "api.troostwijkauctions.com").
- */
- private String extractHost(URI uri) {
- return uri.getHost() != null ? uri.getHost() : uri.toString();
- }
-
- /**
- * Gets statistics for all hosts.
- */
- public Map getAllStats() {
- return Map.copyOf(requestStats);
- }
-
- /**
- * Gets statistics for a specific host.
- */
- public RequestStats getStats(String host) {
- return requestStats.get(host);
- }
-
- /**
- * Rate limiter implementation using token bucket algorithm.
- * Allows burst traffic up to maxRequestsPerSecond, then enforces steady rate.
- */
- private static class RateLimiter {
- private final Semaphore semaphore;
- private final int maxRequestsPerSecond;
- private final long intervalNanos;
-
- RateLimiter(int maxRequestsPerSecond) {
- this.maxRequestsPerSecond = maxRequestsPerSecond;
- this.intervalNanos = TimeUnit.SECONDS.toNanos(1) / maxRequestsPerSecond;
- this.semaphore = new Semaphore(maxRequestsPerSecond);
-
- // Refill tokens periodically
- startRefillThread();
- }
-
- void acquire() throws InterruptedException {
- semaphore.acquire();
-
- // Enforce minimum delay between requests
- long delayMillis = intervalNanos / 1_000_000;
- if (delayMillis > 0) {
- Thread.sleep(delayMillis);
- }
- }
-
- private void startRefillThread() {
- Thread refillThread = new Thread(() -> {
- while (!Thread.currentThread().isInterrupted()) {
- try {
- Thread.sleep(1000); // Refill every second
- int toRelease = maxRequestsPerSecond - semaphore.availablePermits();
- if (toRelease > 0) {
- semaphore.release(toRelease);
- }
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- break;
- }
- }
- }, "RateLimiter-Refill");
- refillThread.setDaemon(true);
- refillThread.start();
- }
- }
-
- /**
- * Statistics tracker for HTTP requests per host.
- */
- public static class RequestStats {
- private final String host;
- private final AtomicLong totalRequests = new AtomicLong(0);
- private final AtomicLong successfulRequests = new AtomicLong(0);
- private final AtomicLong failedRequests = new AtomicLong(0);
- private final AtomicLong rateLimitedRequests = new AtomicLong(0);
- private final AtomicLong totalDurationMs = new AtomicLong(0);
-
- RequestStats(String host) {
- this.host = host;
- }
-
- void incrementTotal() {
- totalRequests.incrementAndGet();
- }
-
- void recordSuccess(long durationMs) {
- successfulRequests.incrementAndGet();
- totalDurationMs.addAndGet(durationMs);
- }
-
- void incrementFailed() {
- failedRequests.incrementAndGet();
- }
-
- void incrementRateLimited() {
- rateLimitedRequests.incrementAndGet();
- }
-
- // Getters
- public String getHost() { return host; }
- public long getTotalRequests() { return totalRequests.get(); }
- public long getSuccessfulRequests() { return successfulRequests.get(); }
- public long getFailedRequests() { return failedRequests.get(); }
- public long getRateLimitedRequests() { return rateLimitedRequests.get(); }
- public long getAverageDurationMs() {
- long successful = successfulRequests.get();
- return successful > 0 ? totalDurationMs.get() / successful : 0;
- }
-
- @Override
- public String toString() {
- return String.format("%s: %d total, %d success, %d failed, %d rate-limited, avg %dms",
- host, getTotalRequests(), getSuccessfulRequests(),
- getFailedRequests(), getRateLimitedRequests(), getAverageDurationMs());
- }
- }
-}
diff --git a/src/main/java/auctiora/TroostwijkMonitor.java b/src/main/java/auctiora/TroostwijkMonitor.java
index c6a9657..90f6763 100644
--- a/src/main/java/auctiora/TroostwijkMonitor.java
+++ b/src/main/java/auctiora/TroostwijkMonitor.java
@@ -18,8 +18,8 @@ public class TroostwijkMonitor {
private static final String LOT_API = "https://api.troostwijkauctions.com/lot/7/list";
- RateLimitedHttpClient2 httpClient;
- ObjectMapper objectMapper;
+ RateLimitedHttpClient httpClient;
+ ObjectMapper objectMapper;
@Getter DatabaseService db;
NotificationService notifier;
ObjectDetectionService detector;
@@ -38,7 +38,7 @@ public class TroostwijkMonitor {
String classNamesPath)
throws SQLException, IOException {
- httpClient = new RateLimitedHttpClient2();
+ httpClient = new RateLimitedHttpClient();
objectMapper = new ObjectMapper();
db = new DatabaseService(databasePath);
notifier = new NotificationService(notificationConfig);
diff --git a/src/main/java/auctiora/WorkflowOrchestrator.java b/src/main/java/auctiora/WorkflowOrchestrator.java
index 299ff6a..f72735e 100644
--- a/src/main/java/auctiora/WorkflowOrchestrator.java
+++ b/src/main/java/auctiora/WorkflowOrchestrator.java
@@ -1,7 +1,6 @@
package auctiora;
import lombok.extern.slf4j.Slf4j;
-import java.io.Console;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
@@ -43,7 +42,7 @@ public class WorkflowOrchestrator {
this.notifier = new NotificationService(notificationConfig);
this.detector = new ObjectDetectionService(yoloCfg, yoloWeights, yoloClasses);
- RateLimitedHttpClient2 httpClient = new RateLimitedHttpClient2();
+ RateLimitedHttpClient httpClient = new RateLimitedHttpClient();
this.imageProcessor = new ImageProcessingService(db, detector, httpClient);
this.monitor = new TroostwijkMonitor(databasePath, notificationConfig,
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 3ddd872..4a5d553 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -31,6 +31,10 @@ quarkus.log.console.level=INFO
%dev.quarkus.log.console.level=DEBUG
%dev.quarkus.live-reload.instrumentation=true
+# JVM Arguments for native access (Jansi, OpenCV, etc.)
+quarkus.native.additional-build-args=--enable-native-access=ALL-UNNAMED
+quarkus.jvm.args=--enable-native-access=ALL-UNNAMED
+
# Production optimizations
%prod.quarkus.package.type=fast-jar
%prod.quarkus.http.enable-compression=true
diff --git a/src/test/java/auctiora/ImageProcessingServiceTest.java b/src/test/java/auctiora/ImageProcessingServiceTest.java
index 442c8a2..e7f8093 100644
--- a/src/test/java/auctiora/ImageProcessingServiceTest.java
+++ b/src/test/java/auctiora/ImageProcessingServiceTest.java
@@ -19,14 +19,14 @@ class ImageProcessingServiceTest {
private DatabaseService mockDb;
private ObjectDetectionService mockDetector;
- private RateLimitedHttpClient2 mockHttpClient;
+ private RateLimitedHttpClient mockHttpClient;
private ImageProcessingService service;
@BeforeEach
void setUp() {
mockDb = mock(DatabaseService.class);
mockDetector = mock(ObjectDetectionService.class);
- mockHttpClient = mock(RateLimitedHttpClient2.class);
+ mockHttpClient = mock(RateLimitedHttpClient.class);
service = new ImageProcessingService(mockDb, mockDetector, mockHttpClient);
}
@@ -102,6 +102,7 @@ class ImageProcessingServiceTest {
ArgumentCaptor lotIdCaptor = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor urlCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor filePathCaptor = ArgumentCaptor.forClass(String.class);
+ @SuppressWarnings("unchecked")
ArgumentCaptor> labelsCaptor = ArgumentCaptor.forClass(List.class);
when(mockDetector.detectObjects(anyString()))
@@ -169,8 +170,8 @@ class ImageProcessingServiceTest {
@DisplayName("Should handle database errors during image save")
void testDatabaseErrorHandling() throws Exception {
// Mock successful HTTP download
- @SuppressWarnings("unchecked")
- var mockResponse = mock(java.net.http.HttpResponse.class);
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ var mockResponse = (java.net.http.HttpResponse) mock(java.net.http.HttpResponse.class);
when(mockResponse.statusCode()).thenReturn(200);
when(mockResponse.body()).thenReturn(new byte[]{1, 2, 3});
when(mockHttpClient.sendGetBytes(anyString())).thenReturn(mockResponse);
diff --git a/src/test/java/auctiora/IntegrationTest.java b/src/test/java/auctiora/IntegrationTest.java
index 22479c6..95e5df0 100644
--- a/src/test/java/auctiora/IntegrationTest.java
+++ b/src/test/java/auctiora/IntegrationTest.java
@@ -48,7 +48,7 @@ class IntegrationTest {
"non_existent.txt"
);
- RateLimitedHttpClient2 httpClient = new RateLimitedHttpClient2();
+ RateLimitedHttpClient httpClient = new RateLimitedHttpClient();
imageProcessor = new ImageProcessingService(db, detector, httpClient);
monitor = new TroostwijkMonitor(