Skip to content

Commit 7cdf29c

Browse files
committed
Refactor DatabaseService to use HTTP API for InfluxDB
1 parent d29582a commit 7cdf29c

File tree

3 files changed

+104
-143
lines changed

3 files changed

+104
-143
lines changed

api/build.gradle.kts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@ repositories {
1515
mavenCentral()
1616
}
1717

18-
dependencies {
19-
api("com.influxdb:influxdb-client-java:7.2.0")
20-
}
21-
2218
publishing {
2319
publications {
2420
create<MavenPublication>("maven") {
Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
package it.renvins.serverpulse.api.service;
22

3-
import com.influxdb.client.InfluxDBClient;
4-
import com.influxdb.client.WriteApi;
3+
import java.util.concurrent.CompletableFuture;
54

65
public interface IDatabaseService extends Service {
76

7+
/**
8+
* Writes a line protocol string to the InfluxDB instance.
9+
* @param lineProtocol The line protocol string to write.
10+
* @return A CompletableFuture that completes with true if the write was successful, false otherwise.
11+
*/
12+
CompletableFuture<Boolean> writeLineProtocol(String lineProtocol);
13+
814
/**
915
* Performs a health check ping to the InfluxDB instance.
1016
* Should only be called internally by connect() or dedicated health checks.
@@ -18,30 +24,11 @@ public interface IDatabaseService extends Service {
1824
*/
1925
boolean isConnected();
2026

21-
/**
22-
* Connects to the InfluxDB instance using the configured settings.
23-
* This method should be called before performing any database operations.
24-
*/
25-
void disconnect();
26-
2727
/**
2828
* Disconnects from the InfluxDB instance and cleans up resources.
2929
* This method should be called when the application is shutting down
3030
* or when the connection is no longer needed.
3131
*/
3232
void startRetryTaskIfNeeded();
3333

34-
/**
35-
* Gets the configured InfluxDB client instance.
36-
*
37-
* @return The InfluxDBClient.
38-
*/
39-
InfluxDBClient getClient();
40-
41-
/**
42-
* Gets the InfluxDB Write API instance for sending data points.
43-
*
44-
* @return The WriteApi.
45-
*/
46-
WriteApi getWriteApi();
4734
}

common/src/main/java/it/renvins/serverpulse/common/DatabaseService.java

Lines changed: 96 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,19 @@
11
package it.renvins.serverpulse.common;
22

3-
import java.net.SocketTimeoutException;
43
import java.net.URI;
54
import java.net.http.HttpClient;
65
import java.net.http.HttpRequest;
76
import java.net.http.HttpResponse;
7+
import java.nio.charset.StandardCharsets;
88
import java.time.Duration;
9+
import java.util.concurrent.CompletableFuture;
910

10-
import com.influxdb.client.InfluxDBClient;
11-
import com.influxdb.client.InfluxDBClientFactory;
12-
import com.influxdb.client.WriteApi;
1311
import it.renvins.serverpulse.api.service.IDatabaseService;
1412
import it.renvins.serverpulse.common.config.DatabaseConfiguration;
1513
import it.renvins.serverpulse.common.logger.PulseLogger;
1614
import it.renvins.serverpulse.common.platform.Platform;
1715
import it.renvins.serverpulse.common.scheduler.Task;
1816
import it.renvins.serverpulse.common.scheduler.TaskScheduler;
19-
import lombok.Getter;
2017

2118
public class DatabaseService implements IDatabaseService {
2219

@@ -28,9 +25,6 @@ public class DatabaseService implements IDatabaseService {
2825

2926
private HttpClient httpClient; // Keep for ping
3027

31-
@Getter private InfluxDBClient client;
32-
@Getter private WriteApi writeApi;
33-
3428
private volatile Task retryTask; // volatile for visibility across threads
3529

3630
private final int MAX_RETRIES = 5;
@@ -40,12 +34,15 @@ public class DatabaseService implements IDatabaseService {
4034
private volatile boolean isConnected = false;
4135
private volatile int retryCount = 0;
4236

37+
//HTTP API endpoints
38+
private String pingUrl;
39+
private String writeUrl;
40+
4341
public DatabaseService(PulseLogger logger, Platform platform, DatabaseConfiguration configuration, TaskScheduler scheduler) {
4442
this.logger = logger;
4543
this.platform = platform;
4644

4745
this.configuration = configuration;
48-
4946
this.scheduler = scheduler;
5047

5148
this.httpClient = HttpClient.newBuilder()
@@ -61,6 +58,17 @@ public void load() {
6158
return;
6259
}
6360
logger.info("Connecting to InfluxDB...");
61+
62+
// Initialize the HttpClient for ping
63+
String baseUrl = configuration.getHost();
64+
if (!baseUrl.endsWith("/")) {
65+
baseUrl += "/";
66+
}
67+
68+
this.pingUrl = baseUrl + "ping";
69+
this.writeUrl = baseUrl + "api/v2/write?org=" + configuration.getOrg() +
70+
"&bucket=" + configuration.getBucket() + "&precision=ns";
71+
6472
scheduler.runAsync(this::connect);
6573
}
6674

@@ -75,44 +83,57 @@ public void unload() {
7583
}
7684

7785
@Override
78-
public boolean ping() {
79-
String url = configuration.getHost();
80-
if (url == null || url.isEmpty()) {
81-
logger.error("InfluxDB URL is missing for ping...");
82-
return false;
86+
public CompletableFuture<Boolean> writeLineProtocol(String lineProtocol) {
87+
if (!isConnected) {
88+
return CompletableFuture.completedFuture(false);
8389
}
8490

85-
// Ensure httpClient is initialized
86-
if (this.httpClient == null) {
87-
logger.error("HttpClient not initialized for ping...");
88-
this.httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
89-
}
90-
91-
HttpRequest request;
9291
try {
93-
String pingUrl = url.endsWith("/") ? url + "ping" : url + "/ping";
94-
request = HttpRequest.newBuilder()
95-
.uri(URI.create(pingUrl))
96-
.GET()
97-
.timeout(Duration.ofSeconds(5)) // Add timeout specific to ping
98-
.build();
99-
} catch (IllegalArgumentException e) {
100-
logger.error("Invalid InfluxDB URL format for ping: " + url, e);
101-
return false;
92+
HttpRequest request = HttpRequest.newBuilder()
93+
.uri(URI.create(writeUrl))
94+
.header("Authorization", "Token " + configuration.getToken())
95+
.header("Content-Type", "text/plain; charset=utf-8")
96+
.POST(HttpRequest.BodyPublishers.ofString(lineProtocol, StandardCharsets.UTF_8))
97+
.timeout(Duration.ofSeconds(10))
98+
.build();
99+
100+
return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
101+
.thenApply(response -> {
102+
if (response.statusCode() == 204) {
103+
return true;
104+
} else {
105+
logger.error("Failed to write to InfluxDB: " + response.statusCode() + " - " + response.body());
106+
return false;
107+
}
108+
})
109+
.exceptionally(throwable -> {
110+
logger.error("Error writing to InfluxDB: " + throwable.getMessage());
111+
if (throwable.getCause() instanceof java.net.ConnectException) {
112+
// Connection lost, trigger reconnection
113+
this.isConnected = false;
114+
startRetryTaskIfNeeded();
115+
}
116+
return false;
117+
});
118+
} catch (Exception e) {
119+
logger.error("Failed to create write request: " + e.getMessage());
120+
return CompletableFuture.completedFuture(false);
102121
}
122+
}
103123

124+
@Override
125+
public boolean ping() {
104126
try {
105-
HttpResponse<Void> response = this.httpClient.send(request, HttpResponse.BodyHandlers.discarding());
127+
HttpRequest request = HttpRequest.newBuilder()
128+
.uri(URI.create(pingUrl))
129+
.GET()
130+
.timeout(Duration.ofSeconds(5))
131+
.build();
132+
133+
HttpResponse<Void> response = httpClient.send(request, HttpResponse.BodyHandlers.discarding());
106134
return response.statusCode() == 204;
107-
} catch (java.net.ConnectException | java.net.UnknownHostException e) {
108-
logger.warning("InfluxDB service is offline...");
109-
return false;
110-
} catch (
111-
SocketTimeoutException e) {
112-
logger.warning("InfluxDB ping timed out: " + e.getMessage());
113-
return false;
114135
} catch (Exception e) {
115-
logger.error("Error during InfluxDB ping: " + e.getMessage(), e);
136+
logger.warning("InfluxDB ping failed: " + e.getMessage());
116137
return false;
117138
}
118139
}
@@ -123,84 +144,6 @@ public boolean isConnected() {
123144
return this.isConnected;
124145
}
125146

126-
/**
127-
* Attempts to connect to InfluxDB. Updates the internal connection status
128-
* and starts the retry task if connection fails.
129-
* Should be run asynchronously.
130-
*/
131-
private void connect() {
132-
// If already connected, don't try again unless forced (e.g., by retry task)
133-
// Note: This check might prevent the retry task from running if isConnected is true
134-
// but the connection actually dropped without detection. We rely on ping() inside here.
135-
136-
// Ensure previous resources are closed if attempting a new connection
137-
// This might be needed if connect() is called manually or by retry.
138-
disconnect();
139-
140-
String url = configuration.getHost();
141-
String token = configuration.getToken();
142-
String org = configuration.getOrg();
143-
String bucket = configuration.getBucket();
144-
145-
try {
146-
logger.info("Attempting to connect to InfluxDB at " + url);
147-
client = InfluxDBClientFactory.create(url, token.toCharArray(), org, bucket);
148-
149-
// Ping immediately after creating client to verify reachability & auth
150-
boolean isPingSuccessful = ping(); // Use the internal ping method
151-
152-
if (isPingSuccessful) {
153-
writeApi = client.makeWriteApi(); // Initialize Write API
154-
155-
this.isConnected = true;
156-
this.retryCount = 0; // Reset retry count on successful connection
157-
158-
stopRetryTask(); // Stop retrying if we just connected
159-
logger.info("Successfully connected to InfluxDB and ping successful...");
160-
} else {
161-
// Ping failed after client creation
162-
logger.warning("Created InfluxDB instance, but ping failed. Will retry...");
163-
this.isConnected = false; // Ensure status is false
164-
165-
if (client != null) {
166-
client.close(); // Close the client if ping failed
167-
client = null;
168-
}
169-
startRetryTaskIfNeeded(); // Start retry task
170-
}
171-
} catch (Exception e) {
172-
// Handle exceptions during InfluxDBClientFactory.create() or ping()
173-
logger.error("Failed to connect or ping InfluxDB: " + e.getMessage());
174-
this.isConnected = false;
175-
if (client != null) { // Ensure client is closed on exception
176-
client.close();
177-
client = null;
178-
}
179-
startRetryTaskIfNeeded(); // Start retry task
180-
}
181-
}
182-
183-
@Override
184-
public void disconnect() {
185-
if (writeApi != null) {
186-
try {
187-
writeApi.close();
188-
} catch (Exception e) {
189-
logger.error("Error closing InfluxDB WriteApi...", e);
190-
}
191-
writeApi = null;
192-
}
193-
if (client != null) {
194-
try {
195-
client.close();
196-
} catch (Exception e) {
197-
logger.error("Error closing InfluxDB Client...", e);
198-
}
199-
client = null;
200-
}
201-
this.isConnected = false;
202-
}
203-
204147

205148
@Override
206149
public synchronized void startRetryTaskIfNeeded() {
@@ -248,6 +191,42 @@ public synchronized void startRetryTaskIfNeeded() {
248191
}
249192

250193

194+
/**
195+
* Attempts to connect to InfluxDB. Updates the internal connection status
196+
* and starts the retry task if connection fails.
197+
* Should be run asynchronously.
198+
*/
199+
private void connect() {
200+
disconnect();
201+
202+
try {
203+
logger.info("Attempting to connect to InfluxDB via HTTP API...");
204+
boolean isPingSuccessful = ping(); // Use the internal ping method
205+
206+
if (isPingSuccessful) {
207+
this.isConnected = true;
208+
this.retryCount = 0; // Reset retry count on successful connection
209+
210+
stopRetryTask(); // Stop retrying if we just connected
211+
logger.info("Successfully connected to InfluxDB and ping successful...");
212+
} else {
213+
// Ping failed after client creation
214+
logger.warning("Created InfluxDB instance, but ping failed. Will retry...");
215+
this.isConnected = false; // Ensure status is false
216+
startRetryTaskIfNeeded(); // Start retry task
217+
}
218+
} catch (Exception e) {
219+
// Handle exceptions during InfluxDBClientFactory.create() or ping()
220+
logger.error("Failed to connect or ping InfluxDB: " + e.getMessage());
221+
this.isConnected = false;
222+
startRetryTaskIfNeeded(); // Start retry task
223+
}
224+
}
225+
226+
private void disconnect() {
227+
this.isConnected = false;
228+
}
229+
251230
/** Stops and nullifies the retry task if it's running. */
252231
private synchronized void stopRetryTask() {
253232
if (retryTask != null) {
@@ -262,7 +241,6 @@ private synchronized void stopRetryTask() {
262241
}
263242
}
264243

265-
266244
/**
267245
* Checks if the essential connection data is present in the config.
268246
* @return true if data seems present, false otherwise.

0 commit comments

Comments
 (0)