Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement ConnectionProvider #8

Merged
merged 42 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
da08c43
implement ConnectionProvider
NathanQingyangXu Oct 25, 2024
2c90afc
change minimal MongoDB version to 6
NathanQingyangXu Oct 28, 2024
cdba9c5
Update src/test/resources/hibernate.properties
NathanQingyangXu Nov 1, 2024
ad28ab1
remove package-info.java in test folder;
NathanQingyangXu Nov 1, 2024
bb51724
improve Javadoc for MongoConnectionProvider per review comments
NathanQingyangXu Nov 1, 2024
5fb9c5e
remove unnecessary codec setting stuff
NathanQingyangXu Nov 1, 2024
08a9603
simplify the dummy implementation of the three methods irrelevant to …
NathanQingyangXu Nov 1, 2024
77b0a72
simplify MongoConnectionProvider per code review comments
NathanQingyangXu Nov 1, 2024
46e06e4
remove exception package and move ConfigurationException to cfg package
NathanQingyangXu Nov 1, 2024
d68a0db
add missing Javadocs for MongoDialect
NathanQingyangXu Nov 1, 2024
b816075
add MQL acronym expansion in Javadoc
NathanQingyangXu Nov 1, 2024
e151069
round #2 code review comments
NathanQingyangXu Nov 4, 2024
bbd90f8
change mininum mongodb version to v6.3
NathanQingyangXu Nov 4, 2024
4036693
use HibernateException instead of ConfigurationException.java
NathanQingyangXu Nov 4, 2024
9c9622c
switch MongoDialect minimum db version to 6.0
NathanQingyangXu Nov 5, 2024
a97f2dd
Update src/main/java/com/mongodb/hibernate/jdbc/MongoConnectionProvid…
NathanQingyangXu Nov 5, 2024
4f9f958
deleted unused constructors in MongoDialect
NathanQingyangXu Nov 5, 2024
9b817a5
add Javadoc for the root package-info.java
NathanQingyangXu Nov 5, 2024
6ad89a0
make more exception messages begin with uppercase
NathanQingyangXu Nov 6, 2024
cdf49b0
update various gradle plugin to latest versions
NathanQingyangXu Nov 6, 2024
a73cf04
add sl4j logging dependencies and logback-test.xml config
NathanQingyangXu Nov 6, 2024
5c11f6c
fix a static check issue
NathanQingyangXu Nov 6, 2024
e366ea6
change default constructor of MongoDialect to end up with null value;…
NathanQingyangXu Nov 7, 2024
452c559
some minor improvements
NathanQingyangXu Nov 8, 2024
2124619
change as per Valentin's excellent code review comments
NathanQingyangXu Nov 8, 2024
196880e
update SessionFactoryTests as per code review comments
NathanQingyangXu Nov 9, 2024
8ac4c4f
update MongoDialect as per code review comments
NathanQingyangXu Nov 9, 2024
8e5eb59
update MongoConnectionProvider as per code review comments
NathanQingyangXu Nov 9, 2024
4756ea9
delete .editorconfig file and move it to another separate PR
NathanQingyangXu Nov 12, 2024
5a7ebed
fix a static check style issue
NathanQingyangXu Nov 12, 2024
51e3184
resolve some code review comments
NathanQingyangXu Nov 12, 2024
5307610
fix style checking issue
NathanQingyangXu Nov 12, 2024
7d11aa1
minor changes to improve Dialect min version usage
NathanQingyangXu Nov 13, 2024
8b3eefe
improve mentioning of MongoDB Java Driver in MongoDialect's Javadoc
NathanQingyangXu Nov 13, 2024
250673b
add transient keyword to MongoClient in MongoConnectionProvider
NathanQingyangXu Nov 13, 2024
b7c37ca
Update src/main/java/com/mongodb/hibernate/jdbc/MongoConnectionProvid…
NathanQingyangXu Nov 13, 2024
089ba52
change as per code review comments
NathanQingyangXu Nov 13, 2024
8e0d8e9
remove database checking logic
NathanQingyangXu Nov 13, 2024
1af2884
fix import style issue
NathanQingyangXu Nov 13, 2024
8dd11e2
change per some code review comments
NathanQingyangXu Nov 13, 2024
e831a88
Merge branch 'main' into HIBERNATE-27
NathanQingyangXu Nov 14, 2024
b9c98ff
add missing 'compileJava' gradle task in evergreen's static-check.sh
NathanQingyangXu Nov 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .evergreen/static-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ source java-config.sh
echo "mongo-hibernate: static checking ..."

./gradlew -version
./gradlew -PxmlReports.enabled=true --info -x test clean check
./gradlew -PxmlReports.enabled=true --info -x test clean check compileJava
15 changes: 7 additions & 8 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,38 +17,38 @@
import net.ltgt.gradle.errorprone.errorprone

plugins {
// Apply the java-library plugin for API and implementation separation.
`java-library`
alias(libs.plugins.spotless)
alias(libs.plugins.errorprone)
}

repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}

dependencies {
// Use JUnit Jupiter for testing.
testImplementation(libs.junit.jupiter)
testImplementation(libs.logback.classic)

testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testRuntimeOnly(libs.junit.platform.launcher)

errorprone(libs.nullaway)
api(libs.jspecify)

errorprone(libs.google.errorprone.core)

implementation(libs.hibernate.core)
implementation(libs.mongo.java.driver.sync)
implementation(libs.sl4j.api)
}

// Apply a specific Java toolchain to ease working on different environments.
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}

tasks.named<Test>("test") {
// Use JUnit Platform for unit tests.
useJUnitPlatform()
}

Expand All @@ -57,8 +57,7 @@ tasks.named<Test>("test") {

spotless {
java {
// note: you can use an empty string for all the imports you didn't specify explicitly, '|' to join group without blank line, and '\\#` prefix for static imports
importOrder("java|javax", "", "\\#")
importOrder()

removeUnusedImports()

Expand Down
19 changes: 14 additions & 5 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,28 @@
# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format

[versions]
junit-jupiter = "5.10.3"
junit-jupiter = "5.11.3"
spotless = "6.25.0"
palantir = "2.50.0"
errorprone = "4.0.1"
google-errorprone-core = "2.9.0"
nullaway = "0.10.26"
jspecify = "0.3.0"
errorprone = "4.1.0"
google-errorprone-core = "2.35.1"
nullaway = "0.12.1"
jspecify = "1.0.0"
hibernate-core = "6.6.1.Final"
mongo-java-driver-sync = "5.2.0"
slf4j-api = "2.0.16"
logback-classic = "1.5.12"

[libraries]
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" }
junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher" }
nullaway = { module = "com.uber.nullaway:nullaway", version.ref = "nullaway" }
jspecify = { module = "org.jspecify:jspecify", version.ref = "jspecify" }
google-errorprone-core = { module = "com.google.errorprone:error_prone_core", version.ref = "google-errorprone-core" }
hibernate-core = { module = "org.hibernate.orm:hibernate-core", version.ref = "hibernate-core" }
mongo-java-driver-sync = { module = "org.mongodb:mongodb-driver-sync", version.ref = "mongo-java-driver-sync" }
sl4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j-api" }
logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback-classic" }

[plugins]
spotless = { id = "com.diffplug.spotless", version.ref = "spotless" }
Expand Down
52 changes: 52 additions & 0 deletions src/main/java/com/mongodb/hibernate/dialect/MongoDialect.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright 2024-present MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.mongodb.hibernate.dialect;

import org.hibernate.dialect.DatabaseVersion;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;

/**
* A MongoDB {@link Dialect} for {@linkplain #getMinimumSupportedVersion() version 6.0 and above}.
*
* <p>Usually Hibernate dialect represents some SQL RDBMS and speaks SQL with vendor-specific difference. MongoDB is a
* document DB and speaks <i>MQL</i> (MongoDB Query Language), but it is still possible to integrate with Hibernate by
* creating a JDBC adaptor on top of <a href="https://www.mongodb.com/docs/drivers/java/sync/current/">MongoDB Java
* Driver</a>.
*/
public final class MongoDialect extends Dialect {
private static final DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make(6);

/** Default constructor used when no version info is available. */
public MongoDialect() {
super((DatabaseVersion) null);
}

/**
* Constructor used when MongoDB meta data is available.
*
* @param info MongoDB meta data
*/
public MongoDialect(DialectResolutionInfo info) {
super(info);
}

@Override
protected DatabaseVersion getMinimumSupportedVersion() {
return MINIMUM_VERSION;
}
}
NathanQingyangXu marked this conversation as resolved.
Show resolved Hide resolved
20 changes: 20 additions & 0 deletions src/main/java/com/mongodb/hibernate/dialect/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright 2024-present MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

@NullMarked
package com.mongodb.hibernate.dialect;

import org.jspecify.annotations.NullMarked;
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright 2024-present MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.mongodb.hibernate.internal;

import java.io.Serial;

/**
* A temporary marker exception to denote that the feature in question is in the scope of MongoDB dialect but has not
* been implemented yet.
*
* <p>Ultimately all of its references should be eliminated sooner or later, and then this class is supposed to be
* deleted prior to product release.
*
* <p>It is recommended to provide some message to explain when it will be implemented (e.g. JIRA ticket id is a good
* idea), but that is optional.
*
* <p>This class is not part of the public API and may be removed or changed at any time.
*/
public final class NotYetImplementedException extends RuntimeException {

@Serial
private static final long serialVersionUID = 1L;

/**
* Default constructor.
*
* <p>It is recommended to use the other constructor with some explanation.
*/
public NotYetImplementedException() {}

/**
* Constructor with message parameter.
*
* @param message explanation on when the feature is to be implemented
*/
public NotYetImplementedException(String message) {
super(message);
}
}
20 changes: 20 additions & 0 deletions src/main/java/com/mongodb/hibernate/internal/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright 2024-present MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

@NullMarked
package com.mongodb.hibernate.internal;

import org.jspecify.annotations.NullMarked;
153 changes: 153 additions & 0 deletions src/main/java/com/mongodb/hibernate/jdbc/MongoConnectionProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* Copyright 2024-present MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.mongodb.hibernate.jdbc;

import static org.hibernate.cfg.JdbcSettings.JAKARTA_JDBC_PASSWORD;
import static org.hibernate.cfg.JdbcSettings.JAKARTA_JDBC_URL;
import static org.hibernate.cfg.JdbcSettings.JAKARTA_JDBC_USER;

import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.hibernate.internal.NotYetImplementedException;
import java.io.IOException;
import java.io.NotSerializableException;
import java.io.ObjectOutputStream;
import java.io.Serial;
import java.sql.Connection;
import java.util.Map;
import org.hibernate.HibernateException;
import org.hibernate.cfg.JdbcSettings;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.service.UnknownUnwrapTypeException;
import org.hibernate.service.spi.Configurable;
import org.hibernate.service.spi.Stoppable;
import org.jspecify.annotations.Nullable;

/**
* {@linkplain com.mongodb.hibernate.dialect.MongoDialect MongoDB dialect}'s customized JDBC {@link ConnectionProvider}
* SPI implementation.
*
* <p>{@link MongoConnectionProvider} uses the following Hibernate properties:
*
* <table>
* <tr><th>Property</th><th>Description</th><th>Required</th></tr>
* <tr>
* <td>{@value JdbcSettings#JAKARTA_JDBC_URL}</td>
* <td>MongoDB
* <a href="https://www.mongodb.com/docs/manual/reference/connection-string/">connection string</a>,
* which must specify the database name for authentication
* if {@value JdbcSettings#JAKARTA_JDBC_USER} is specified.</td>
* <td>✓</td>
* </tr>
* <tr>
* <td>{@value JdbcSettings#JAKARTA_JDBC_USER}</td>
* <td>{@code userName} for {@link com.mongodb.MongoCredential#createCredential(String, String, char[])}</td>
* <td></td>
* </tr>
* <tr>
* <td>{@value JdbcSettings#JAKARTA_JDBC_PASSWORD}</td>
* <td>{@code password} for {@link com.mongodb.MongoCredential#createCredential(String, String, char[])}</td>
* <td></td>
* </tr>
* </table>
*
* @see ConnectionProvider
* @see JdbcSettings#JAKARTA_JDBC_URL
* @see JdbcSettings#JAKARTA_JDBC_USER
* @see JdbcSettings#JAKARTA_JDBC_PASSWORD
* @see <a href="https://www.mongodb.com/docs/manual/reference/connection-string/">connection string</a>
*/
public final class MongoConnectionProvider implements ConnectionProvider, Configurable, Stoppable {

@Serial
private static final long serialVersionUID = 1L;

private @Nullable MongoClient mongoClient;
stIncMale marked this conversation as resolved.
Show resolved Hide resolved
stIncMale marked this conversation as resolved.
Show resolved Hide resolved

@Override
public Connection getConnection() {
throw new NotYetImplementedException(
"To be implemented in scope of https://jira.mongodb.org/browse/HIBERNATE-29");
}

@Override
public void closeConnection(Connection connection) {
throw new NotYetImplementedException(
"To be implemented in scope of https://jira.mongodb.org/browse/HIBERNATE-29");
}

@Override
public boolean supportsAggressiveRelease() {
return false;
}

@Override
public boolean isUnwrappableAs(Class<?> unwrapType) {
jyemin marked this conversation as resolved.
Show resolved Hide resolved
return false;
}

@Override
public <T> T unwrap(Class<T> unwrapType) {
throw new UnknownUnwrapTypeException(unwrapType);
}

@Override
public void configure(Map<String, Object> configValues) {
var jdbcUrl = configValues.get(JAKARTA_JDBC_URL);
if (jdbcUrl == null) {
throw new HibernateException("Configuration is required: " + JAKARTA_JDBC_URL);
}
if (!(jdbcUrl instanceof String)) {
throw new HibernateException(
String.format("Configuration [%s] value [%s] not of string type", JAKARTA_JDBC_URL, jdbcUrl));
}
ConnectionString connectionString;
try {
connectionString = new ConnectionString((String) jdbcUrl);
} catch (RuntimeException e) {
throw new HibernateException(
String.format(
"Failed to create ConnectionString from configuration [%s] with value [%s]",
JAKARTA_JDBC_URL, jdbcUrl),
e);
}

var clientSettingsBuilder = MongoClientSettings.builder().applyConnectionString(connectionString);

if (configValues.get(JAKARTA_JDBC_USER) != null || configValues.get(JAKARTA_JDBC_PASSWORD) != null) {
throw new NotYetImplementedException("To be implemented after auth could be tested in CI");
}

var clientSettings = clientSettingsBuilder.build();
NathanQingyangXu marked this conversation as resolved.
Show resolved Hide resolved
jyemin marked this conversation as resolved.
Show resolved Hide resolved
this.mongoClient = MongoClients.create(clientSettings);
}

@Override
public void stop() {
if (this.mongoClient != null) {
this.mongoClient.close();
}
}

@Serial
private void writeObject(ObjectOutputStream out) throws IOException {
throw new NotSerializableException(
"This class is not designed to be serialized despite it having to implement `Serializable`");
}
}
Loading