General idea

This commit is contained in:
Braydon 2023-12-12 00:16:47 -05:00
parent 139a1123f2
commit 7a0de46329
7 changed files with 335 additions and 1 deletions

16
pom.xml

@ -146,8 +146,22 @@
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.3-jre</version>
<scope>compile</scope>
</dependency>
<!-- Databases -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>4.11.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

@ -0,0 +1,20 @@
package me.braydon.feather;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
/**
* The Feather thread pool. This is used by default
* when executing tasks in asynchronous pipelines.
*
* @author Braydon
*/
public final class FeatherThreads {
private static final AtomicInteger ID = new AtomicInteger(0); // The thread id
public static final ExecutorService THREAD_POOL = Executors.newFixedThreadPool(4, new ThreadFactoryBuilder()
.setNameFormat("Feather Thread #" + (ID.incrementAndGet()))
.build()); // The thread pool to execute on
}

@ -0,0 +1,64 @@
package me.braydon.feather;
import lombok.NonNull;
import java.io.Closeable;
/**
* Represents a database.
*
* @author Braydon
* @param <B> the bootstrap class of this database
* @param <A> the type of credentials this database uses
* @param <P> the type of pipeline for this database
* @param <AP> the type of async pipeline for this database
*/
public interface IDatabase<B, A, P, AP> extends Closeable {
/**
* Get the name of this database.
*
* @return the database name
*/
@NonNull String getName();
/**
* Initialize a connection to this database.
*
* @param credentials the optional credentials to use
*/
void connect(A credentials);
/**
* Check if this database is connected.
*
* @return the database connection state
*/
boolean isConnected();
/**
* Get the bootstrap class
* instance for this database.
*
* @return the bootstrap class instance, null if none
* @see B for bootstrap class
*/
B getBootstrap();
/**
* Get the synchronized
* pipeline for this database.
*
* @return the synchronized pipeline
* @see P for synchronized pipeline
*/
@NonNull P sync();
/**
* Get the asynchronous
* pipeline for this database.
*
* @return the asynchronous pipeline
* @see AP for asynchronous pipeline
*/
@NonNull AP async();
}

@ -0,0 +1,26 @@
package me.braydon.feather;
import com.mongodb.ConnectionString;
import me.braydon.feather.databases.mongodb.MongoDB;
/**
* @author Braydon
*/
public final class Playground {
public static void main(String[] args) {
MongoDB mongoDB = new MongoDB(); // Create the database instance
mongoDB.connect(new ConnectionString("mongodb://root:p4$$w0rd@localhost:27017/database")); // Connect to the MongoDB server
// Get the ping to the database synchronously (blocking)
long ping = mongoDB.sync().getPing();
System.out.println("ping = " + ping);
// ...or get the ping to the database asynchronously (another thread)
mongoDB.async().getPing().thenAccept(asyncPing -> {
System.out.println("asyncPing = " + asyncPing);
});
// Close the connection after our app is done
mongoDB.close();
}
}

@ -0,0 +1,51 @@
package me.braydon.feather.databases.mongodb;
import com.mongodb.BasicDBObject;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import me.braydon.feather.FeatherThreads;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
/**
* The pipeline for handling asynchronous {@link MongoDB} operations.
*
* @author Braydon
*/
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public final class MongoAsyncPipeline {
/**
* The database to handle operations for.
*/
@NonNull private final MongoDB database;
/**
* Get the latency to the database.
*
* @return the latency
*/
public CompletableFuture<Long> getPing() {
return getPing(FeatherThreads.THREAD_POOL);
}
/**
* Get the latency to the database.
*
* @param executor the thread executor to use
* @return the latency
* @see Executor for executor
*/
public CompletableFuture<Long> getPing(@NonNull Executor executor) {
return CompletableFuture.supplyAsync(() -> {
if (!database.isConnected()) { // Not connected
return -1L;
}
// Return ping
long before = System.currentTimeMillis();
database.getDatabase().runCommand(new BasicDBObject("ping", "1"));
return System.currentTimeMillis() - before;
}, executor);
}
}

@ -0,0 +1,125 @@
package me.braydon.feather.databases.mongodb;
import com.mongodb.ConnectionString;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
import lombok.Getter;
import lombok.NonNull;
import me.braydon.feather.IDatabase;
/**
* The {@link IDatabase} implementation for MongoDB.
*
* @author Braydon
* @see MongoClient for the bootstrap class
* @see ConnectionString for the credentials class
* @see MongoSyncPipeline for the sync pipeline class
* @see MongoAsyncPipeline for the async pipeline class
* @see <a href="https://www.mongodb.com/">MongoDB Official Site</a>
*/
public class MongoDB implements IDatabase<MongoClient, ConnectionString, MongoSyncPipeline, MongoAsyncPipeline> {
/**
* The current {@link MongoClient} instance.
*/
private MongoClient client;
/**
* The {@link MongoDatabase} instance.
*/
@Getter private MongoDatabase database;
/**
* Get the name of this database.
*
* @return the database name
*/
@Override @NonNull
public String getName() {
return "MongoDB";
}
/**
* Initialize a connection to this database.
*
* @param credentials the optional credentials to use
*/
@Override
public void connect(ConnectionString credentials) {
if (credentials == null) { // We need valid credentials
throw new IllegalArgumentException("No credentials defined");
}
if (isConnected()) { // Already connected
throw new IllegalStateException("Already connected");
}
String databaseName = credentials.getDatabase(); // Get the database name
if (databaseName == null) {
throw new IllegalArgumentException("A database name is required");
}
if (client != null) { // We have a client, close it first
client.close();
}
client = MongoClients.create(credentials); // Create a new client
database = client.getDatabase(databaseName); // Get the database
}
/**
* Check if this database is connected.
*
* @return the database connection state
*/
@Override
public boolean isConnected() {
return client != null;
}
/**
* Get the bootstrap class
* instance for this database.
*
* @return the bootstrap class instance, null if none
* @see MongoClients for bootstrap class
*/
@Override
public MongoClient getBootstrap() {
return client;
}
/**
* Get the synchronized
* pipeline for this database.
*
* @return the synchronized pipeline
* @see MongoSyncPipeline for synchronized pipeline
*/
@Override @NonNull
public MongoSyncPipeline sync() {
return new MongoSyncPipeline(this);
}
/**
* Get the asynchronous
* pipeline for this database.
*
* @return the asynchronous pipeline
* @see MongoAsyncPipeline for asynchronous pipeline
*/
@Override @NonNull
public MongoAsyncPipeline async() {
return new MongoAsyncPipeline(this);
}
/**
* Closes this stream and releases any system resources associated
* with it. If the stream is already closed then invoking this
* method has no effect.
*/
@Override
public void close() {
if (client != null) {
client.close();
}
client = null;
database = null;
}
}

@ -0,0 +1,34 @@
package me.braydon.feather.databases.mongodb;
import com.mongodb.BasicDBObject;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.NonNull;
/**
* The pipeline for handling {@link MongoDB} operations.
*
* @author Braydon
*/
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public final class MongoSyncPipeline {
/**
* The database to handle operations for.
*/
@NonNull private final MongoDB database;
/**
* Get the latency to the database.
*
* @return the latency
*/
public long getPing() {
if (!database.isConnected()) { // Not connected
return -1L;
}
// Return ping
long before = System.currentTimeMillis();
database.getDatabase().runCommand(new BasicDBObject("ping", "1"));
return System.currentTimeMillis() - before;
}
}