Compare commits

..

4 Commits

Author SHA1 Message Date
ea314415dc Merge pull request 'chore(deps): update dependency org.apache.maven.plugins:maven-shade-plugin to v3.5.2' (#1) from renovate/org.apache.maven.plugins-maven-shade-plugin-3.x into master
Some checks failed
Publish Release / Publish Release (ubuntu-latest, 8, 3.9.4) (push) Failing after 15s
Reviewed-on: #1
2024-03-17 19:25:20 -07:00
0c7e35c42f Merge pull request 'fix(deps): update dependency io.lettuce:lettuce-core to v6.3.2.release' (#2) from renovate/io.lettuce-lettuce-core-6.x into master
Some checks failed
Publish Release / Publish Release (ubuntu-latest, 8, 3.9.4) (push) Has been cancelled
Reviewed-on: #2
2024-03-17 19:25:06 -07:00
Renovate Bot
2cf4b63e2e fix(deps): update dependency io.lettuce:lettuce-core to v6.3.2.release 2024-03-18 02:21:26 +00:00
Renovate Bot
2723d6cd45 chore(deps): update dependency org.apache.maven.plugins:maven-shade-plugin to v3.5.2 2024-03-18 02:21:24 +00:00
19 changed files with 85 additions and 548 deletions

@ -2,7 +2,7 @@ name: Publish Release
on: on:
push: push:
branches: [master, develop] branches: [master]
tags: tags:
- '*' - '*'
@ -58,13 +58,14 @@ jobs:
# Publish to Maven # Publish to Maven
- name: Publish to Maven - name: Publish to Maven
run: mvn deploy -Pgen-javadocs -B -Dstyle.color=always --update-snapshots -T6C -e run: |
chmod +x ./scripts/deploy.sh
./scripts/deploy.sh
# Generate changelog # Generate changelog
- name: Generate Changelog - name: Generate Changelog
id: changelog id: changelog
uses: https://github.com/mathiasvr/command-output@v2.0.0 uses: https://github.com/mathiasvr/command-output@v2.0.0
if: ${{ github.ref.action == 'master' }}
with: with:
run: | run: |
npx conventional-changelog-cli -p angular -i CHANGELOG.md -s --skip-unstable npx conventional-changelog-cli -p angular -i CHANGELOG.md -s --skip-unstable
@ -75,7 +76,6 @@ jobs:
# so we can use it later # so we can use it later
- name: Extract Maven project version - name: Extract Maven project version
shell: bash -l {0} shell: bash -l {0}
if: ${{ github.ref.action == 'master' }}
run: | run: |
RELEASE_VERSION=$(mvn -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec) RELEASE_VERSION=$(mvn -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec)
echo "Project version: $RELEASE_VERSION" echo "Project version: $RELEASE_VERSION"
@ -85,7 +85,6 @@ jobs:
- name: Publish Release - name: Publish Release
id: publish-release id: publish-release
uses: https://git.rainnny.club/Rainnny/release-action@main uses: https://git.rainnny.club/Rainnny/release-action@main
if: ${{ github.ref.action == 'master' }}
with: with:
api_key: ${{ secrets.RELEASE_ACCESS_TOKEN }} api_key: ${{ secrets.RELEASE_ACCESS_TOKEN }}
tag: Feather-${{ env.RELEASE_VERSION }} tag: Feather-${{ env.RELEASE_VERSION }}

1
.gitignore vendored

@ -18,7 +18,6 @@ cmake-build-*/
out/ out/
build/ build/
work/ work/
target/
.idea_modules/ .idea_modules/
atlassian-ide-plugin.xml atlassian-ide-plugin.xml
com_crashlytics_export_strings.xml com_crashlytics_export_strings.xml

@ -91,6 +91,9 @@
<!-- See: https://checkstyle.org/checks/javadoc/javadocmissingwhitespaceafterasterisk.html#JavadocMissingWhitespaceAfterAsterisk --> <!-- See: https://checkstyle.org/checks/javadoc/javadocmissingwhitespaceafterasterisk.html#JavadocMissingWhitespaceAfterAsterisk -->
<module name="JavadocMissingWhitespaceAfterAsterisk" /> <module name="JavadocMissingWhitespaceAfterAsterisk" />
<!-- See: https://checkstyle.org/checks/javadoc/javadocparagraph.html#JavadocParagraph -->
<module name="JavadocParagraph" />
<!-- See: https://checkstyle.org/checks/javadoc/javadocstyle.html#JavadocStyle --> <!-- See: https://checkstyle.org/checks/javadoc/javadocstyle.html#JavadocStyle -->
<module name="JavadocStyle" /> <module name="JavadocStyle" />
@ -212,6 +215,11 @@
<!-- See: https://checkstyle.org/checks/javadoc/singlelinejavadoc.html#SingleLineJavadoc --> <!-- See: https://checkstyle.org/checks/javadoc/singlelinejavadoc.html#SingleLineJavadoc -->
<module name="SingleLineJavadoc" /> <module name="SingleLineJavadoc" />
<!-- See: https://checkstyle.org/checks/javadoc/requireemptylinebeforeblocktaggroup.html#RequireEmptyLineBeforeBlockTagGroup -->
<module name="RequireEmptyLineBeforeBlockTagGroup">
<property name="violateExecutionOnNonTightHtml" value="true" />
</module>
<!-- See: https://checkstyle.org/checks/whitespace/singlespaceseparator.html#SingleSpaceSeparator --> <!-- See: https://checkstyle.org/checks/whitespace/singlespaceseparator.html#SingleSpaceSeparator -->
<module name="SingleSpaceSeparator" /> <module name="SingleSpaceSeparator" />

51
pom.xml

@ -7,7 +7,7 @@
<!--Project Details--> <!--Project Details-->
<groupId>me.braydon</groupId> <groupId>me.braydon</groupId>
<artifactId>Feather</artifactId> <artifactId>Feather</artifactId>
<version>1.0.1-dev</version> <version>1.0.0</version>
<properties> <properties>
<java.version>8</java.version> <java.version>8</java.version>
@ -18,7 +18,7 @@
<build> <build>
<plugins> <plugins>
<!-- Used for compiling the source code with the proper Java version --> <!--Used for compiling the source code with the proper Java version-->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
@ -27,17 +27,17 @@
<source>${java.version}</source> <source>${java.version}</source>
<target>${java.version}</target> <target>${java.version}</target>
<!-- Enable incremental builds, this is reversed due to --> <!--Enable incremental builds, this is reversed due to-->
<!-- a bug as seen in https://issues.apache.org/jira/browse/MCOMPILER-209 --> <!--a bug as seen in https://issues.apache.org/jira/browse/MCOMPILER-209-->
<useIncrementalCompilation>false</useIncrementalCompilation> <useIncrementalCompilation>false</useIncrementalCompilation>
</configuration> </configuration>
</plugin> </plugin>
<!-- Handles shading of dependencies in the final output jar --> <!--Handles shading of dependencies in the final output jar-->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>3.5.1</version> <version>3.5.2</version>
<configuration> <configuration>
<createDependencyReducedPom>false</createDependencyReducedPom> <createDependencyReducedPom>false</createDependencyReducedPom>
</configuration> </configuration>
@ -70,7 +70,7 @@
</executions> </executions>
</plugin> </plugin>
<!-- Used for generating a git properties file during build --> <!--Used for generating a git properties file during build-->
<plugin> <plugin>
<groupId>pl.project13.maven</groupId> <groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId> <artifactId>git-commit-id-plugin</artifactId>
@ -168,13 +168,6 @@
</repository> </repository>
</distributionManagement> </distributionManagement>
<repositories>
<repository>
<id>rainnny-repo-public</id>
<url>https://maven.rainnny.club/public</url>
</repository>
</repositories>
<!-- Depends --> <!-- Depends -->
<dependencies> <dependencies>
<dependency> <dependency>
@ -184,15 +177,15 @@
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.google.code.gson</groupId> <groupId>com.google.guava</groupId>
<artifactId>gson</artifactId> <artifactId>guava</artifactId>
<version>2.10.1</version> <version>32.1.3-jre</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.netty</groupId> <groupId>com.google.code.gson</groupId>
<artifactId>netty-common</artifactId> <artifactId>gson</artifactId>
<version>4.1.104.Final</version> <version>2.10.1</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
@ -201,25 +194,13 @@
<groupId>org.mongodb</groupId> <groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId> <artifactId>mongodb-driver-sync</artifactId>
<version>4.11.1</version> <version>4.11.1</version>
<scope>provided</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.lettuce</groupId> <groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId> <artifactId>lettuce-core</artifactId>
<version>7.0.0-SNAPSHOT</version> <version>6.3.2.RELEASE</version>
<scope>provided</scope> <scope>compile</scope>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>4.0.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>3.3.1</version>
<scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

7
scripts/deploy.sh Normal file

@ -0,0 +1,7 @@
#!/bin/bash
# Switch dir
cd "$(dirname "$0")/.." || exit
# Deploy
mvn deploy -Pgen-javadocs -B -Dstyle.color=always --update-snapshots -T6C -e

@ -1,20 +0,0 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.feather.annotation;
import java.lang.annotation.*;
/**
* {@link Field}'s tagged with this annotation
* will be set to the raw data from the document
* it is in.
*
* @author Braydon
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented @Inherited
public @interface RawData { }

@ -7,17 +7,16 @@ package me.braydon.feather.data;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.ToString; import lombok.ToString;
import me.braydon.feather.FeatherSettings; import me.braydon.feather.FeatherSettings;
import me.braydon.feather.annotation.Field; import me.braydon.feather.annotation.Field;
import me.braydon.feather.annotation.Id; import me.braydon.feather.annotation.Id;
import me.braydon.feather.annotation.RawData;
import me.braydon.feather.annotation.Serializable; import me.braydon.feather.annotation.Serializable;
import me.braydon.feather.common.FieldUtils; import me.braydon.feather.common.FieldUtils;
import me.braydon.feather.common.Tuple; import me.braydon.feather.common.Tuple;
import me.braydon.feather.database.IDatabase; import me.braydon.feather.database.IDatabase;
import javax.annotation.concurrent.ThreadSafe;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
@ -30,9 +29,10 @@ import java.util.UUID;
* document is universal between all {@link IDatabase}'s. * document is universal between all {@link IDatabase}'s.
* *
* @author Braydon * @author Braydon
* @param <V> the type of value this document holds
*/ */
@Getter @ToString @ThreadSafe @Getter @ToString
public class Document { public class Document<V> {
/** /**
* The key to use for the id field. * The key to use for the id field.
*/ */
@ -51,20 +51,15 @@ public class Document {
* a tuple that contains the Java field, as well as * a tuple that contains the Java field, as well as
* the field value. * the field value.
* </p> * </p>
*
* @see V for value type
*/ */
private final Map<String, Tuple<java.lang.reflect.Field, Object>> mappedData = Collections.synchronizedMap(new LinkedHashMap<>()); private final Map<String, Tuple<java.lang.reflect.Field, V>> mappedData = Collections.synchronizedMap(new LinkedHashMap<>());
@SneakyThrows
public Document(@NonNull Object element) { public Document(@NonNull Object element) {
Class<?> clazz = element.getClass(); // Get the element class Class<?> clazz = element.getClass(); // Get the element class
String idKey = null; // The key for the id field String idKey = null; // The key for the id field
java.lang.reflect.Field rawDataField = null; // The raw data field if defined
for (java.lang.reflect.Field field : clazz.getDeclaredFields()) { for (java.lang.reflect.Field field : clazz.getDeclaredFields()) {
// Raw data field, save it for later
if (field.isAnnotationPresent(RawData.class)) {
rawDataField = field;
continue;
}
// Field is missing the @Field annotation, skip it // Field is missing the @Field annotation, skip it
if (!field.isAnnotationPresent(Field.class)) { if (!field.isAnnotationPresent(Field.class)) {
continue; continue;
@ -77,6 +72,7 @@ public class Document {
idKey = key; idKey = key;
} }
Class<?> fieldType = field.getType(); // The type of the field Class<?> fieldType = field.getType(); // The type of the field
try {
Object value = field.get(element); // The value of the field Object value = field.get(element); // The value of the field
if (field.isAnnotationPresent(Serializable.class)) { // Serialize the field if @Serializable is present if (field.isAnnotationPresent(Serializable.class)) { // Serialize the field if @Serializable is present
@ -85,16 +81,15 @@ public class Document {
value = value.toString(); value = value.toString();
} }
mappedData.put(key, new Tuple<>(field, value)); // Store in our map mappedData.put(key, new Tuple<>(field, (V) value)); // Store in our map
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
}
} }
assert idKey != null; // We need an id key assert idKey != null; // We need an id key
if (rawDataField != null) { // We have a raw data field, set it
rawDataField.setAccessible(true); // Make our field accessible
rawDataField.set(element, toMappedData()); // Set the raw data field to our mapped document
}
this.idKey = idKey; // Set our id key this.idKey = idKey; // Set our id key
Tuple<java.lang.reflect.Field, Object> key = mappedData.get(idKey); // Get the id from the data map Tuple<java.lang.reflect.Field, V> key = mappedData.get(idKey); // Get the id from the data map
if (key == null) { // The element is missing an id field if (key == null) { // The element is missing an id field
throw new IllegalArgumentException("No @Id annotated field found in " + clazz.getSimpleName()); throw new IllegalArgumentException("No @Id annotated field found in " + clazz.getSimpleName());
} }
@ -108,9 +103,9 @@ public class Document {
* @see #mappedData for stored data * @see #mappedData for stored data
*/ */
@NonNull @NonNull
public Map<String, Object> toMappedData() { public Map<String, V> toMappedData() {
Map<String, Object> mappedData = new LinkedHashMap<>(); // The mapped data Map<String, V> mappedData = new LinkedHashMap<>(); // The mapped data
for (Map.Entry<String, Tuple<java.lang.reflect.Field, Object>> entry : this.mappedData.entrySet()) { for (Map.Entry<String, Tuple<java.lang.reflect.Field, V>> entry : this.mappedData.entrySet()) {
mappedData.put(entry.getKey(), entry.getValue().getRight()); mappedData.put(entry.getKey(), entry.getValue().getRight());
} }
return mappedData; return mappedData;

@ -29,7 +29,6 @@ public interface IDatabase<B, C> extends Closeable {
* *
* @param credentials the optional credentials to use * @param credentials the optional credentials to use
* @throws IllegalStateException if already connected * @throws IllegalStateException if already connected
* @see C for credentials
*/ */
void connect(C credentials) throws IllegalStateException; void connect(C credentials) throws IllegalStateException;

@ -10,7 +10,6 @@ import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import me.braydon.feather.FeatherSettings; import me.braydon.feather.FeatherSettings;
import me.braydon.feather.annotation.RawData;
import me.braydon.feather.annotation.Serializable; import me.braydon.feather.annotation.Serializable;
import me.braydon.feather.common.FieldUtils; import me.braydon.feather.common.FieldUtils;
@ -22,7 +21,7 @@ import java.util.Map;
import java.util.UUID; import java.util.UUID;
/** /**
* A repository belonging to an {@link IDatabase}. * A repository belonging to a {@link IDatabase}.
* *
* @author Braydon * @author Braydon
* @param <D> the database this repository uses * @param <D> the database this repository uses
@ -121,18 +120,8 @@ public abstract class Repository<D extends IDatabase<?, ?>, ID, E> {
Constructor<? extends E> constructor = entityClass.getConstructor(); // Get the no args constructor Constructor<? extends E> constructor = entityClass.getConstructor(); // Get the no args constructor
E entity = constructor.newInstance(); // Create the entity E entity = constructor.newInstance(); // Create the entity
// Get the field tagged with @Field // Get the field tagged with @Id
java.lang.reflect.Field rawDataField = null; // The raw data field if defined
for (Field field : entityClass.getDeclaredFields()) { for (Field field : entityClass.getDeclaredFields()) {
// Raw data field, save it for later
if (field.isAnnotationPresent(RawData.class)) {
rawDataField = field;
continue;
}
// Not the field we're looking for
if (!field.isAnnotationPresent(me.braydon.feather.annotation.Field.class)) {
continue;
}
String key = FieldUtils.extractKey(field); // The key of the database field String key = FieldUtils.extractKey(field); // The key of the database field
Class<?> type = field.getType(); // The type of the field Class<?> type = field.getType(); // The type of the field
Object value = mappedData.get(key); // The value of the field Object value = mappedData.get(key); // The value of the field
@ -148,10 +137,6 @@ public abstract class Repository<D extends IDatabase<?, ?>, ID, E> {
field.setAccessible(true); field.setAccessible(true);
field.set(entity, value); field.set(entity, value);
} }
if (rawDataField != null) { // We have a raw data field, set it
rawDataField.setAccessible(true); // Make our field accessible
rawDataField.set(entity, mappedData); // Set the raw data field to our mapped document
}
return entity; return entity;
} catch (NoSuchMethodException ex) { // We need our no args constructor } catch (NoSuchMethodException ex) { // We need our no args constructor
throw new IllegalStateException("Entity " + entityClass.getName() + " is missing no args constructor"); throw new IllegalStateException("Entity " + entityClass.getName() + " is missing no args constructor");

@ -1,110 +0,0 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.feather.database.impl.mariadb;
import com.zaxxer.hikari.HikariDataSource;
import lombok.NonNull;
import me.braydon.feather.database.IDatabase;
/**
* The {@link IDatabase} implementation for MariaDB (and other MySQL servers).
*
* @author Braydon
* @see HikariDataSource for the bootstrap class
* @see MariaDBAuthorization for the credentials class
* @see <a href="https://github.com/brettwooldridge/HikariCP">HikariCP Official GitHub</a>
*/
public class MariaDB implements IDatabase<HikariDataSource, MariaDBAuthorization> {
/**
* The current {@link HikariDataSource} instance.
*/
private HikariDataSource dataSource;
/**
* Get the name of this database.
*
* @return the database name
*/
@Override @NonNull
public String getName() {
return "MariaDB";
}
/**
* Initialize a connection to this database.
*
* @param credentials the optional credentials to use
* @throws IllegalArgumentException if no credentials are provided
* @throws IllegalStateException if already connected
* @see MariaDBAuthorization for credentials
*/
@Override
public void connect(MariaDBAuthorization credentials) throws IllegalArgumentException, IllegalStateException {
// if (credentials == null) { // We need valid credentials
// throw new IllegalArgumentException("No credentials defined");
// }
// if (isConnected()) { // Already connected
// throw new IllegalStateException("Already connected");
// }
// if (dataSource != null) { // We have a data source, close it first
// dataSource.close();
// }
// HikariConfig config = credentials.getHikariConfig(); // Get the custom config
// if (config == null) { // No custom config, make a new one
// config = new HikariConfig();
// }
// config.setJdbcUrl(credentials.getJdbcUrl()); // Set the JDBC connection URL
// config.setUsername(credentials.getUsername()); // Set the username
// config.setPassword(credentials.getPassword()); // Set the password
// dataSource = new HikariDataSource(config); // Create a new data source
throw new UnsupportedOperationException(); // Not impl yet
}
/**
* Check if this database is connected.
*
* @return the database connection state
*/
@Override
public boolean isConnected() {
return dataSource != null && (dataSource.isRunning() && !dataSource.isClosed());
}
/**
* Get the latency to this database.
*
* @return the latency, -1 if not connected
*/
@Override
public long getLatency() {
return 0;
}
/**
* Get the bootstrap class
* instance for this database.
*
* @return the bootstrap class instance, null if none
* @see HikariDataSource for bootstrap class
*/
@Override
public HikariDataSource getBootstrap() {
return dataSource;
}
/**
* 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 (dataSource != null) {
dataSource.close();
}
dataSource = null;
}
}

@ -1,37 +0,0 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.feather.database.impl.mariadb;
import com.zaxxer.hikari.HikariConfig;
import lombok.*;
/**
* Authorization for a {@link MariaDB} database.
*
* @author Braydon
*/
@RequiredArgsConstructor @AllArgsConstructor @Getter @ToString
public final class MariaDBAuthorization {
/**
* The JDBC connection URL for the database.
*/
@NonNull private final String jdbcUrl;
/**
* The username to use for authenticating with the database.
*/
@NonNull private final String username;
/**
* The password to use for authenticating with the database.
*/
@NonNull private final String password;
/**
* The optional {@link HikariConfig} to use.
*/
private HikariConfig hikariConfig;
}

@ -1,93 +0,0 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.feather.database.impl.mariadb;
import lombok.NonNull;
import me.braydon.feather.database.Repository;
import java.util.List;
/**
* The {@link MariaDB} {@link Repository} implementation.
*
* @author Braydon
* @param <ID> the identifier for type for entities
* @param <E> the entity type this repository stores
*/
public class MariaDBRepository<ID, E> extends Repository<MariaDB, ID, E> {
public MariaDBRepository(@NonNull MariaDB database, @NonNull Class<? extends E> entityClass) {
super(database, entityClass);
}
/**
* Get the entity with the given id.
*
* @param id the entity id
* @return the entity with the id, null if none
* @see ID for id
* @see E for entity
*/
@Override
public E find(@NonNull ID id) {
throw new UnsupportedOperationException();
}
/**
* Get all entities within this repository.
*
* @return the entities
* @see E for entity
*/
@Override
public List<E> findAll() {
throw new UnsupportedOperationException();
}
/**
* Save the given entities.
*
* @param entities the entities to save
* @see E for entity
*/
@Override
public void saveAll(@NonNull E... entities) {
throw new UnsupportedOperationException();
}
/**
* Get the amount of stored entities.
*
* @return the amount of stored entities
* @see E for entity
*/
@Override
public long count() {
throw new UnsupportedOperationException();
}
/**
* Drop the entity with the given id.
*
* @param id the entity id to drop
* @see ID for id
* @see E for entity
*/
@Override
public void dropById(@NonNull ID id) {
throw new UnsupportedOperationException();
}
/**
* Drop the given entity.
*
* @param entity the entity to drop
* @see E for entity
*/
@Override
public void drop(@NonNull E entity) {
throw new UnsupportedOperationException();
}
}

@ -1,80 +0,0 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.feather.database.impl.mariadb.queries;
import lombok.Builder;
import lombok.Getter;
import lombok.NonNull;
import lombok.Singular;
import me.braydon.feather.database.impl.mariadb.MariaDB;
import java.util.Map;
/**
* A builder for {@link MariaDB} update queries.
*
* @author Braydon
*/
@Builder @Getter
public class UpdateQuery {
/**
* The table to execute the update in.
*/
@NonNull private String table;
/**
* The mapped values (by column) for this query.
*/
@NonNull @Singular private Map<String, Object> values;
/**
* The where clause for this query.
*/
@NonNull private WhereClause whereClause;
/**
* Build this query.
*
* @return the built query
*/
@Override @NonNull
public String toString() {
// Build the values
StringBuilder valuesBuilder = new StringBuilder();
for (Map.Entry<String, Object> entry : values.entrySet()) {
valuesBuilder.append("`").append(entry.getKey()).append("`")
.append("='").append(entry.getValue()).append("', ");
}
String values = valuesBuilder.toString(); // The built values string
values = values.substring(0, values.length() - 2); // Remove the trailing comma
// Build the query
return String.format("UPDATE `%s` SET %s WHERE %s;",
table, values, whereClause
);
}
/**
* The where clause for this query.
*/
@Builder @Getter
public static class WhereClause {
/**
* The column to target.
*/
@NonNull private String column;
/**
* The value of the column to target.
*/
@NonNull private Object value;
@Override @NonNull
public String toString() {
return column + "='" + value + "'";
}
}
}

@ -49,7 +49,6 @@ public class MongoDB implements IDatabase<MongoClient, ConnectionString> {
* @param credentials the optional credentials to use * @param credentials the optional credentials to use
* @throws IllegalArgumentException if no credentials or database name is provided * @throws IllegalArgumentException if no credentials or database name is provided
* @throws IllegalStateException if already connected * @throws IllegalStateException if already connected
* @see ConnectionString for credentials
*/ */
@Override @Override
public void connect(ConnectionString credentials) throws IllegalArgumentException, IllegalStateException { public void connect(ConnectionString credentials) throws IllegalArgumentException, IllegalStateException {
@ -92,8 +91,8 @@ public class MongoDB implements IDatabase<MongoClient, ConnectionString> {
} }
// Return ping // Return ping
long before = System.currentTimeMillis(); long before = System.currentTimeMillis();
database.runCommand(new BasicDBObject("ping", "1")); // Send a ping command database.runCommand(new BasicDBObject("ping", "1"));
return System.currentTimeMillis() - before; // Return time difference return System.currentTimeMillis() - before;
} }
/** /**
@ -124,7 +123,7 @@ public class MongoDB implements IDatabase<MongoClient, ConnectionString> {
if (!isConnected()) { // Not connected if (!isConnected()) { // Not connected
throw new IllegalStateException("Not connected"); throw new IllegalStateException("Not connected");
} }
return new MongoRepository<>(this, entityClass, collectionName); return new MongoRepository<>(this, entityClass, database.getCollection(collectionName));
} }
/** /**

@ -12,16 +12,16 @@ import com.mongodb.client.model.Indexes;
import com.mongodb.client.model.UpdateOneModel; import com.mongodb.client.model.UpdateOneModel;
import com.mongodb.client.model.UpdateOptions; import com.mongodb.client.model.UpdateOptions;
import lombok.NonNull; import lombok.NonNull;
import lombok.SneakyThrows;
import me.braydon.feather.common.Tuple; import me.braydon.feather.common.Tuple;
import me.braydon.feather.database.Repository; import me.braydon.feather.database.Repository;
import me.braydon.feather.database.impl.mongodb.annotation.CustomDocument;
import me.braydon.feather.database.impl.mongodb.annotation.Index; import me.braydon.feather.database.impl.mongodb.annotation.Index;
import org.bson.Document; import org.bson.Document;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.util.ArrayList;
import java.util.*; import java.util.Collections;
import java.util.List;
import java.util.Map;
/** /**
* The {@link MongoDB} {@link Repository} implementation. * The {@link MongoDB} {@link Repository} implementation.
@ -36,10 +36,6 @@ public class MongoRepository<ID, E> extends Repository<MongoDB, ID, E> {
*/ */
@NonNull private final MongoCollection<Document> collection; @NonNull private final MongoCollection<Document> collection;
public MongoRepository(@NonNull MongoDB database, @NonNull Class<? extends E> entityClass, @NonNull String collectionName) {
this(database, entityClass, database.getDatabase().getCollection(collectionName));
}
public MongoRepository(@NonNull MongoDB database, @NonNull Class<? extends E> entityClass, @NonNull MongoCollection<Document> collection) { public MongoRepository(@NonNull MongoDB database, @NonNull Class<? extends E> entityClass, @NonNull MongoCollection<Document> collection) {
super(database, entityClass); super(database, entityClass);
this.collection = collection; this.collection = collection;
@ -94,29 +90,17 @@ public class MongoRepository<ID, E> extends Repository<MongoDB, ID, E> {
* @param entities the entities to save * @param entities the entities to save
* @see E for entity * @see E for entity
*/ */
@Override @SneakyThrows @Override
public void saveAll(@NonNull E... entities) { public void saveAll(@NonNull E... entities) {
List<UpdateOneModel<Document>> updateModels = new ArrayList<>(); // The update models to bulk write List<UpdateOneModel<Document>> updateModels = new ArrayList<>(); // The update models to bulk write
for (E entity : entities) { for (E entity : entities) {
me.braydon.feather.data.Document document = new me.braydon.feather.data.Document(entity); // Create a document from the entity me.braydon.feather.data.Document<Object> document = new me.braydon.feather.data.Document<>(entity); // Create a document from the entity
Document bsonDocument; // The Bson document to save
Method customDocumentMethod = Arrays.stream(entity.getClass().getDeclaredMethods())
.filter(method -> method.isAnnotationPresent(CustomDocument.class))
.findFirst().orElse(null); // Get the @CustomDocument method
// We have a custom document method
if (customDocumentMethod != null && (customDocumentMethod.getReturnType() == Document.class)) {
bsonDocument = (Document) customDocumentMethod.invoke(entity); // Get our custom document
} else { // Otherwise, use our mapped data
bsonDocument = new Document(document.toMappedData());
}
// Add our update model to the list // Add our update model to the list
updateModels.add(new UpdateOneModel<>( updateModels.add(new UpdateOneModel<>(
Filters.eq(document.getIdKey(), document.getKey()), Filters.eq(document.getIdKey(), document.getKey()),
new Document("$set", bsonDocument), new Document("$set", new Document(document.toMappedData())),
new UpdateOptions().upsert(true) new UpdateOptions().upsert(true)
)); ));
@ -178,7 +162,7 @@ public class MongoRepository<ID, E> extends Repository<MongoDB, ID, E> {
*/ */
@Override @Override
public void drop(@NonNull E entity) { public void drop(@NonNull E entity) {
me.braydon.feather.data.Document document = new me.braydon.feather.data.Document(entity); // Create a document from the entity me.braydon.feather.data.Document<Object> document = new me.braydon.feather.data.Document<>(entity); // Create a document from the entity
collection.deleteOne(new Document(document.getIdKey(), document.getKey())); // Delete the entity collection.deleteOne(new Document(document.getIdKey(), document.getKey())); // Delete the entity
} }
} }

@ -1,21 +0,0 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.feather.database.impl.mongodb.annotation;
import java.lang.annotation.*;
/**
* Methods tagged with this annotation will be invoked
* when a document is being saved to the database, this
* allows is to save a custom document, rather than
* parsing the entity and creating one that way.
*
* @author Braydon
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented @Inherited
public @interface CustomDocument { }

@ -19,7 +19,7 @@ import me.braydon.feather.database.IDatabase;
* @see RedisURI for the credentials class * @see RedisURI for the credentials class
* @see <a href="https://redis.io">Redis Official Site</a> * @see <a href="https://redis.io">Redis Official Site</a>
*/ */
public class Redis implements IDatabase<StatefulRedisConnection<String, String>, String> { public class Redis implements IDatabase<StatefulRedisConnection<String, String>, RedisURI> {
/** /**
* The current {@link RedisClient} instance. * The current {@link RedisClient} instance.
*/ */
@ -43,25 +43,25 @@ public class Redis implements IDatabase<StatefulRedisConnection<String, String>,
/** /**
* Initialize a connection to this database. * Initialize a connection to this database.
* *
* @param uri the optional credentials to use * @param credentials the optional credentials to use
* @throws IllegalArgumentException if no credentials are provided * @throws IllegalArgumentException if no credentials are provided
* @throws IllegalStateException if already connected * @throws IllegalStateException if already connected
*/ */
@Override @Override
public void connect(String uri) throws IllegalArgumentException, IllegalStateException { public void connect(RedisURI credentials) throws IllegalArgumentException, IllegalStateException {
if (uri == null) { // We need valid credentials if (credentials == null) { // We need valid credentials
throw new IllegalArgumentException("No credentials defined"); throw new IllegalArgumentException("No credentials defined");
} }
if (isConnected()) { // Already connected if (isConnected()) { // Already connected
throw new IllegalStateException("Already connected"); throw new IllegalStateException("Already connected");
} }
if (client != null) { // We have a client, close it first if (client != null) { // We have a client, close it first
client.shutdown(); client.close();
} }
if (connection != null) { // We have a connection, close it first if (connection != null) { // We have a connection, close it first
connection.close(); connection.close();
} }
client = RedisClient.create(uri); // Create a new client client = RedisClient.create(credentials); // Create a new client
connection = client.connect(); // Connect to the Redis server connection = client.connect(); // Connect to the Redis server
} }
@ -82,13 +82,7 @@ public class Redis implements IDatabase<StatefulRedisConnection<String, String>,
*/ */
@Override @Override
public long getLatency() { public long getLatency() {
if (!isConnected()) { // Not connected return 0;
return -1L;
}
// Return ping
long before = System.currentTimeMillis();
connection.sync().ping(); // Send a ping command
return System.currentTimeMillis() - before; // Return time difference
} }
/** /**
@ -145,7 +139,7 @@ public class Redis implements IDatabase<StatefulRedisConnection<String, String>,
@Override @Override
public void close() { public void close() {
if (client != null) { if (client != null) {
client.shutdown(); client.close();
} }
if (connection != null) { if (connection != null) {
connection.close(); connection.close();

@ -7,13 +7,12 @@ package me.braydon.feather.database.impl.redis;
import io.lettuce.core.api.sync.RedisCommands; import io.lettuce.core.api.sync.RedisCommands;
import lombok.NonNull; import lombok.NonNull;
import me.braydon.feather.common.Tuple;
import me.braydon.feather.data.Document; import me.braydon.feather.data.Document;
import me.braydon.feather.database.Repository; import me.braydon.feather.database.Repository;
import me.braydon.feather.database.impl.redis.annotation.TTL;
import java.lang.reflect.Field; import java.util.ArrayList;
import java.util.*; import java.util.Collections;
import java.util.List;
/** /**
* The {@link Redis} {@link Repository} implementation. * The {@link Redis} {@link Repository} implementation.
@ -79,24 +78,8 @@ public class RedisRepository<ID, E> extends Repository<Redis, ID, E> {
commands.multi(); commands.multi();
} }
for (E entity : entities) { // Set our entities for (E entity : entities) { // Set our entities
Document document = new Document(entity); // Create a document from the entity Document<String> document = new Document<>(entity); // Create a document from the entity
String key = keyPrefix + ":" + document.getKey(); // The key of this entity commands.hmset(keyPrefix + ":" + document.getKey(), document.toMappedData());
Map<String, String> mappedData = new HashMap<>();
for (Map.Entry<String, Tuple<Field, Object>> entry : document.getMappedData().entrySet()) {
mappedData.put(entry.getKey(), String.valueOf(entry.getValue().getRight()));
}
commands.hmset(key, mappedData); // Set the mapped document in the database
// Handling @TTL annotations
Class<?> clazz = entity.getClass(); // The entity class
if (!clazz.isAnnotationPresent(TTL.class)) { // Missing @TTL
continue;
}
long ttl = clazz.getAnnotation(TTL.class).value(); // Get the ttl value
if (ttl > 0L) { // Value is above zero, set it
commands.expire(key, ttl);
}
} }
if (multi) { // Execute the commands in bulk if (multi) { // Execute the commands in bulk
commands.exec(); commands.exec();
@ -134,7 +117,7 @@ public class RedisRepository<ID, E> extends Repository<Redis, ID, E> {
*/ */
@Override @Override
public void drop(@NonNull E entity) { public void drop(@NonNull E entity) {
me.braydon.feather.data.Document document = new me.braydon.feather.data.Document(entity); // Create a document from the entity me.braydon.feather.data.Document<Object> document = new me.braydon.feather.data.Document<>(entity); // Create a document from the entity
getDatabase().getBootstrap().sync().del(keyPrefix + ":" + document.getKey()); getDatabase().getBootstrap().sync().del(keyPrefix + ":" + document.getKey());
} }
} }

@ -1,35 +0,0 @@
/*
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
*
* For inquiries, please contact braydonrainnny@gmail.com
*/
package me.braydon.feather.database.impl.redis.annotation;
import java.lang.annotation.*;
/**
* Entities tagged with this annotation will
* expire after the amount of defined seconds.
*
* @author Braydon
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented @Inherited
public @interface TTL {
/**
* The time-to-live value.
* <p>
* If the value is zero or
* below, then there will
* be no expiration.
* <p>
* The key this TTL rule is assigned
* to will expire in the defined amount
* of seconds from when the key was updated.
* </p>
*
* @return the value in seconds
*/
long value() default -1L;
}