diff --git a/pom.xml b/pom.xml index d510c40..a442296 100644 --- a/pom.xml +++ b/pom.xml @@ -146,8 +146,22 @@ org.projectlombok lombok - 1.18.28 + 1.18.30 provided + + com.google.guava + guava + 32.1.3-jre + compile + + + + + org.mongodb + mongodb-driver-sync + 4.11.1 + compile + \ No newline at end of file diff --git a/src/main/java/me/braydon/feather/FeatherThreads.java b/src/main/java/me/braydon/feather/FeatherThreads.java new file mode 100644 index 0000000..85a35f7 --- /dev/null +++ b/src/main/java/me/braydon/feather/FeatherThreads.java @@ -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 +} \ No newline at end of file diff --git a/src/main/java/me/braydon/feather/IDatabase.java b/src/main/java/me/braydon/feather/IDatabase.java new file mode 100644 index 0000000..24f366a --- /dev/null +++ b/src/main/java/me/braydon/feather/IDatabase.java @@ -0,0 +1,64 @@ +package me.braydon.feather; + +import lombok.NonNull; + +import java.io.Closeable; + +/** + * Represents a database. + * + * @author Braydon + * @param the bootstrap class of this database + * @param the type of credentials this database uses + * @param

the type of pipeline for this database + * @param the type of async pipeline for this database + */ +public interface IDatabase 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(); +} \ No newline at end of file diff --git a/src/main/java/me/braydon/feather/Playground.java b/src/main/java/me/braydon/feather/Playground.java new file mode 100644 index 0000000..d0c443c --- /dev/null +++ b/src/main/java/me/braydon/feather/Playground.java @@ -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(); + } +} \ No newline at end of file diff --git a/src/main/java/me/braydon/feather/databases/mongodb/MongoAsyncPipeline.java b/src/main/java/me/braydon/feather/databases/mongodb/MongoAsyncPipeline.java new file mode 100644 index 0000000..f88995d --- /dev/null +++ b/src/main/java/me/braydon/feather/databases/mongodb/MongoAsyncPipeline.java @@ -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 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 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); + } +} \ No newline at end of file diff --git a/src/main/java/me/braydon/feather/databases/mongodb/MongoDB.java b/src/main/java/me/braydon/feather/databases/mongodb/MongoDB.java new file mode 100644 index 0000000..53cd4b8 --- /dev/null +++ b/src/main/java/me/braydon/feather/databases/mongodb/MongoDB.java @@ -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 MongoDB Official Site + */ +public class MongoDB implements IDatabase { + /** + * 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; + } +} \ No newline at end of file diff --git a/src/main/java/me/braydon/feather/databases/mongodb/MongoSyncPipeline.java b/src/main/java/me/braydon/feather/databases/mongodb/MongoSyncPipeline.java new file mode 100644 index 0000000..89bbb4f --- /dev/null +++ b/src/main/java/me/braydon/feather/databases/mongodb/MongoSyncPipeline.java @@ -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; + } +} \ No newline at end of file