Compare commits

...

7 Commits

Author SHA1 Message Date
Christoph Strobl
db4a1be5cb Update after review 2023-07-25 10:30:13 +02:00
Christoph Strobl
2e336a12e9 Refine locking. 2023-07-21 13:37:36 +02:00
Christoph Strobl
3e517e9c84 Prepare issue branch. 2023-07-21 13:37:36 +02:00
Julia Lee
e1986373fd Polishing.
Remove duplicate test configuration.

Original Pull Request: #4447
2023-07-17 10:20:59 +02:00
Julia Lee
5407456973 Fix test setup so that temporal conversions use symmetric timezone setting.
Closes: #4446
Original Pull Request: #4447
2023-07-17 10:12:02 +02:00
Mark Paluch
31f0aa348d After release cleanups.
See #4387
2023-07-14 14:57:12 +02:00
Mark Paluch
28abf1c15b Prepare next development iteration.
See #4387
2023-07-14 14:57:10 +02:00
12 changed files with 166 additions and 235 deletions

16
pom.xml
View File

@@ -5,7 +5,7 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>4.2.0-M1</version>
<version>4.2.x-4429-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Spring Data MongoDB</name>
@@ -15,7 +15,7 @@
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>3.2.0-M1</version>
<version>3.2.0-SNAPSHOT</version>
</parent>
<modules>
@@ -26,7 +26,7 @@
<properties>
<project.type>multi</project.type>
<dist.id>spring-data-mongodb</dist.id>
<springdata.commons>3.2.0-M1</springdata.commons>
<springdata.commons>3.2.0-SNAPSHOT</springdata.commons>
<mongo>4.10.2</mongo>
<mongo.reactivestreams>${mongo}</mongo.reactivestreams>
<jmh.version>1.19</jmh.version>
@@ -144,6 +144,16 @@
</dependencies>
<repositories>
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
<repository>
<id>spring-milestone</id>
<url>https://repo.spring.io/milestone</url>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>4.2.0-M1</version>
<version>4.2.x-4429-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -15,7 +15,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>4.2.0-M1</version>
<version>4.2.x-4429-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -13,7 +13,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>4.2.0-M1</version>
<version>4.2.x-4429-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -18,8 +18,12 @@ package org.springframework.data.mongodb.core;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;
import org.bson.Document;
@@ -187,16 +191,19 @@ public interface MongoOperations extends FluentMongoOperations {
return new SessionScoped() {
private final Object lock = new Object();
private @Nullable ClientSession session = null;
private final Lock lock = new ReentrantLock();
private @Nullable ClientSession session;
@Override
public <T> T execute(SessionCallback<T> action, Consumer<ClientSession> onComplete) {
synchronized (lock) {
lock.lock();
try {
if (session == null) {
session = sessionProvider.get();
}
} finally {
lock.unlock();
}
try {

View File

@@ -22,6 +22,9 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Supplier;
import org.aopalliance.intercept.MethodInterceptor;
@@ -134,7 +137,8 @@ public final class LazyLoadingProxyFactory {
}
return prepareProxyFactory(propertyType,
() -> new LazyLoadingInterceptor(property, callback, source, exceptionTranslator)).getProxy(LazyLoadingProxy.class.getClassLoader());
() -> new LazyLoadingInterceptor(property, callback, source, exceptionTranslator))
.getProxy(LazyLoadingProxy.class.getClassLoader());
}
/**
@@ -171,6 +175,8 @@ public final class LazyLoadingProxyFactory {
}
}
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final MongoPersistentProperty property;
private final DbRefResolverCallback callback;
private final Object source;
@@ -339,25 +345,29 @@ public final class LazyLoadingProxyFactory {
}
@Nullable
private synchronized Object resolve() {
private Object resolve() {
if (resolved) {
lock.readLock().lock();
try {
if (resolved) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace(String.format("Accessing already resolved lazy loading property %s.%s",
property.getOwner() != null ? property.getOwner().getName() : "unknown", property.getName()));
if (LOGGER.isTraceEnabled()) {
LOGGER.trace(String.format("Accessing already resolved lazy loading property %s.%s",
property.getOwner() != null ? property.getOwner().getName() : "unknown", property.getName()));
}
return result;
}
return result;
} finally {
lock.readLock().unlock();
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace(String.format("Resolving lazy loading property %s.%s",
property.getOwner() != null ? property.getOwner().getName() : "unknown", property.getName()));
}
try {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace(String.format("Resolving lazy loading property %s.%s",
property.getOwner() != null ? property.getOwner().getName() : "unknown", property.getName()));
}
return callback.resolve(property);
return executeWhileLocked(lock.writeLock(), () -> callback.resolve(property));
} catch (RuntimeException ex) {
DataAccessException translatedException = exceptionTranslator.translateExceptionIfPossible(ex);
@@ -370,6 +380,16 @@ public final class LazyLoadingProxyFactory {
translatedException != null ? translatedException : ex);
}
}
private static <T> T executeWhileLocked(Lock lock, Supplier<T> stuff) {
lock.lock();
try {
return stuff.get();
} finally {
lock.unlock();
}
}
}
}

View File

@@ -18,6 +18,8 @@ package org.springframework.data.mongodb.core.messaging;
import java.time.Duration;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;
import org.springframework.dao.DataAccessResourceFailureException;
@@ -39,7 +41,7 @@ import com.mongodb.client.MongoCursor;
*/
abstract class CursorReadingTask<T, R> implements Task {
private final Object lifecycleMonitor = new Object();
private final Lock lock = new ReentrantLock();
private final MongoTemplate template;
private final SubscriptionRequest<T, R, RequestOptions> request;
@@ -86,19 +88,14 @@ abstract class CursorReadingTask<T, R> implements Task {
}
} catch (InterruptedException e) {
synchronized (lifecycleMonitor) {
state = State.CANCELLED;
}
doWhileLocked(lock, () -> state = State.CANCELLED);
Thread.currentThread().interrupt();
break;
}
}
} catch (RuntimeException e) {
synchronized (lifecycleMonitor) {
state = State.CANCELLED;
}
doWhileLocked(lock, () -> state = State.CANCELLED);
errorHandler.handleError(e);
}
}
@@ -114,30 +111,32 @@ abstract class CursorReadingTask<T, R> implements Task {
*/
private void start() {
synchronized (lifecycleMonitor) {
doWhileLocked(lock, () -> {
if (!State.RUNNING.equals(state)) {
state = State.STARTING;
}
}
});
do {
boolean valid = false;
// boolean valid = false;
synchronized (lifecycleMonitor) {
boolean valid = executeWhileLocked(lock, () -> {
if (State.STARTING.equals(state)) {
MongoCursor<T> cursor = execute(() -> initCursor(template, request.getRequestOptions(), targetType));
valid = isValidCursor(cursor);
if (valid) {
this.cursor = cursor;
state = State.RUNNING;
} else if (cursor != null) {
cursor.close();
}
if (!State.STARTING.equals(state)) {
return false;
}
}
MongoCursor<T> cursor = execute(() -> initCursor(template, request.getRequestOptions(), targetType));
boolean isValid = isValidCursor(cursor);
if (isValid) {
this.cursor = cursor;
state = State.RUNNING;
} else if (cursor != null) {
cursor.close();
}
return isValid;
});
if (!valid) {
@@ -145,9 +144,7 @@ abstract class CursorReadingTask<T, R> implements Task {
Thread.sleep(100);
} catch (InterruptedException e) {
synchronized (lifecycleMonitor) {
state = State.CANCELLED;
}
doWhileLocked(lock, () -> state = State.CANCELLED);
Thread.currentThread().interrupt();
}
}
@@ -163,7 +160,7 @@ abstract class CursorReadingTask<T, R> implements Task {
@Override
public void cancel() throws DataAccessResourceFailureException {
synchronized (lifecycleMonitor) {
doWhileLocked(lock, () -> {
if (State.RUNNING.equals(state) || State.STARTING.equals(state)) {
this.state = State.CANCELLED;
@@ -171,7 +168,7 @@ abstract class CursorReadingTask<T, R> implements Task {
cursor.close();
}
}
}
});
}
@Override
@@ -181,10 +178,7 @@ abstract class CursorReadingTask<T, R> implements Task {
@Override
public State getState() {
synchronized (lifecycleMonitor) {
return state;
}
return executeWhileLocked(lock, () -> state);
}
@Override
@@ -220,13 +214,12 @@ abstract class CursorReadingTask<T, R> implements Task {
@Nullable
private T getNext() {
synchronized (lifecycleMonitor) {
return executeWhileLocked(lock, () -> {
if (State.RUNNING.equals(state)) {
return cursor.tryNext();
}
}
throw new IllegalStateException(String.format("Cursor %s is not longer open", cursor));
throw new IllegalStateException(String.format("Cursor %s is not longer open", cursor));
});
}
private static boolean isValidCursor(@Nullable MongoCursor<?> cursor) {
@@ -263,4 +256,23 @@ abstract class CursorReadingTask<T, R> implements Task {
throw translated != null ? translated : e;
}
}
private static void doWhileLocked(Lock lock, Runnable action) {
executeWhileLocked(lock, () -> {
action.run();
return null;
});
}
@Nullable
private static <T> T executeWhileLocked(Lock lock, Supplier<T> stuff) {
lock.lock();
try {
return stuff.get();
} finally {
lock.unlock();
}
}
}

View File

@@ -20,6 +20,10 @@ import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Supplier;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -35,8 +39,7 @@ import org.springframework.util.ObjectUtils;
/**
* Simple {@link Executor} based {@link MessageListenerContainer} implementation for running {@link Task tasks} like
* listening to MongoDB <a href="https://docs.mongodb.com/manual/changeStreams/">Change Streams</a> and tailable
* cursors.
* <br />
* cursors. <br />
* This message container creates long-running tasks that are executed on {@link Executor}.
*
* @author Christoph Strobl
@@ -49,9 +52,11 @@ public class DefaultMessageListenerContainer implements MessageListenerContainer
private final TaskFactory taskFactory;
private final Optional<ErrorHandler> errorHandler;
private final Object lifecycleMonitor = new Object();
private final Map<SubscriptionRequest, Subscription> subscriptions = new LinkedHashMap<>();
ReadWriteLock lifecycleMonitor = new ReentrantReadWriteLock();
ReadWriteLock subscriptionMonitor = new ReentrantReadWriteLock();
private boolean running = false;
/**
@@ -109,43 +114,34 @@ public class DefaultMessageListenerContainer implements MessageListenerContainer
@Override
public void start() {
synchronized (lifecycleMonitor) {
doWhileLocked(lifecycleMonitor.writeLock(), () -> {
if (!this.running) {
subscriptions.values().stream() //
.filter(it -> !it.isActive()) //
.filter(TaskSubscription.class::isInstance) //
.map(TaskSubscription.class::cast) //
.map(TaskSubscription::getTask) //
.forEach(taskExecutor::execute);
if (this.running) {
return;
running = true;
}
subscriptions.values().stream() //
.filter(it -> !it.isActive()) //
.filter(TaskSubscription.class::isInstance) //
.map(TaskSubscription.class::cast) //
.map(TaskSubscription::getTask) //
.forEach(taskExecutor::execute);
running = true;
}
});
}
@Override
public void stop() {
synchronized (lifecycleMonitor) {
doWhileLocked(lifecycleMonitor.writeLock(), () -> {
if (this.running) {
subscriptions.values().forEach(Cancelable::cancel);
running = false;
}
}
});
}
@Override
public boolean isRunning() {
synchronized (this.lifecycleMonitor) {
return running;
}
return executeWhileLocked(lifecycleMonitor.readLock(), () -> running);
}
@Override
@@ -170,36 +166,32 @@ public class DefaultMessageListenerContainer implements MessageListenerContainer
@Override
public Optional<Subscription> lookup(SubscriptionRequest<?, ?, ?> request) {
synchronized (lifecycleMonitor) {
return Optional.ofNullable(subscriptions.get(request));
}
return executeWhileLocked(subscriptionMonitor.readLock(), () -> Optional.ofNullable(subscriptions.get(request)));
}
public Subscription register(SubscriptionRequest request, Task task) {
Subscription subscription = new TaskSubscription(task);
synchronized (lifecycleMonitor) {
return executeWhileLocked(this.subscriptionMonitor.writeLock(), () ->
{
if (subscriptions.containsKey(request)) {
return subscriptions.get(request);
}
Subscription subscription = new TaskSubscription(task);
this.subscriptions.put(request, subscription);
if (this.running) {
if (this.isRunning()) {
taskExecutor.execute(task);
}
}
return subscription;
});
return subscription;
}
@Override
public void remove(Subscription subscription) {
synchronized (lifecycleMonitor) {
doWhileLocked(this.subscriptionMonitor.writeLock(), () -> {
if (subscriptions.containsValue(subscription)) {
@@ -209,6 +201,25 @@ public class DefaultMessageListenerContainer implements MessageListenerContainer
subscriptions.values().remove(subscription);
}
});
}
private static void doWhileLocked(Lock lock, Runnable action) {
executeWhileLocked(lock, () -> {
action.run();
return null;
});
}
@Nullable
private static <T> T executeWhileLocked(Lock lock, Supplier<T> stuff) {
lock.lock();
try {
return stuff.get();
} finally {
lock.unlock();
}
}

View File

@@ -25,7 +25,6 @@ import java.math.BigInteger;
import java.net.URL;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.*;
@@ -106,6 +105,7 @@ import com.mongodb.DBRef;
* @author Mark Paluch
* @author Roman Puchkovskiy
* @author Heesu Jung
* @author Julia Lee
*/
@ExtendWith(MockitoExtension.class)
class MappingMongoConverterUnitTests {
@@ -2619,7 +2619,7 @@ class MappingMongoConverterUnitTests {
void projectShouldReadSimpleInterfaceProjection() {
org.bson.Document source = new org.bson.Document("birthDate",
Date.from(LocalDate.of(1999, 12, 1).atStartOfDay().toInstant(ZoneOffset.UTC))).append("foo", "Walter");
Date.from(LocalDate.of(1999, 12, 1).atStartOfDay(systemDefault()).toInstant())).append("foo", "Walter");
EntityProjectionIntrospector discoverer = EntityProjectionIntrospector.create(converter.getProjectionFactory(),
EntityProjectionIntrospector.ProjectionPredicate.typeHierarchy()
@@ -2637,7 +2637,7 @@ class MappingMongoConverterUnitTests {
void projectShouldReadSimpleDtoProjection() {
org.bson.Document source = new org.bson.Document("birthDate",
Date.from(LocalDate.of(1999, 12, 1).atStartOfDay().toInstant(ZoneOffset.UTC))).append("foo", "Walter");
Date.from(LocalDate.of(1999, 12, 1).atStartOfDay(systemDefault()).toInstant())).append("foo", "Walter");
EntityProjectionIntrospector introspector = EntityProjectionIntrospector.create(converter.getProjectionFactory(),
EntityProjectionIntrospector.ProjectionPredicate.typeHierarchy()

View File

@@ -70,6 +70,7 @@ import com.mongodb.client.vault.ClientEncryptions;
/**
* @author Christoph Strobl
* @author Julia Lee
*/
public abstract class AbstractEncryptionTestBase {
@@ -450,7 +451,8 @@ public abstract class AbstractEncryptionTestBase {
protected void configureConverters(MongoConverterConfigurationAdapter converterConfigurationAdapter) {
converterConfigurationAdapter
.registerPropertyValueConverterFactory(PropertyValueConverterFactory.beanFactoryAware(applicationContext));
.registerPropertyValueConverterFactory(PropertyValueConverterFactory.beanFactoryAware(applicationContext))
.useNativeDriverJavaTimeCodecs();
}
@Bean

View File

@@ -16,20 +16,10 @@
package org.springframework.data.mongodb.core.encryption;
import java.util.Collections;
import org.bson.BsonBinary;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.convert.PropertyValueConverterFactory;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions.MongoConverterConfigurationAdapter;
import org.springframework.data.mongodb.core.convert.encryption.MongoEncryptionConverter;
import org.springframework.data.mongodb.core.encryption.BypassAutoEncryptionTest.Config;
import org.springframework.data.util.Lazy;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@@ -38,16 +28,15 @@ import com.mongodb.ClientEncryptionSettings;
import com.mongodb.MongoClientSettings.Builder;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.model.vault.DataKeyOptions;
import com.mongodb.client.vault.ClientEncryptions;
/**
* Encryption tests for client having {@link AutoEncryptionSettings#isBypassAutoEncryption()}.
*
* @author Christoph Strobl
* @author Julia Lee
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = Config.class)
@ContextConfiguration(classes = BypassAutoEncryptionTest.Config.class)
public class BypassAutoEncryptionTest extends AbstractEncryptionTestBase {
@Disabled
@@ -59,8 +48,6 @@ public class BypassAutoEncryptionTest extends AbstractEncryptionTestBase {
@Configuration
static class Config extends EncryptionConfig {
@Autowired ApplicationContext applicationContext;
@Override
protected void configureClientSettings(Builder builder) {
@@ -73,31 +60,5 @@ public class BypassAutoEncryptionTest extends AbstractEncryptionTestBase {
.keyVaultNamespace(clientEncryptionSettings.getKeyVaultNamespace()) //
.bypassAutoEncryption(true).build());
}
@Override
protected void configureConverters(MongoConverterConfigurationAdapter converterConfigurationAdapter) {
converterConfigurationAdapter
.registerPropertyValueConverterFactory(PropertyValueConverterFactory.beanFactoryAware(applicationContext));
}
@Bean
@Override
MongoEncryptionConverter encryptingConverter(MongoClientEncryption mongoClientEncryption) {
Lazy<BsonBinary> dataKey = Lazy.of(() -> mongoClientEncryption.getClientEncryption().createDataKey("local",
new DataKeyOptions().keyAltNames(Collections.singletonList("mySuperSecretKey"))));
return new MongoEncryptionConverter(mongoClientEncryption,
EncryptionKeyResolver.annotated((ctx) -> EncryptionKey.keyId(dataKey.get())));
}
@Bean
@Override
CachingMongoClientEncryption clientEncryption(ClientEncryptionSettings encryptionSettings) {
return new CachingMongoClientEncryption(() -> ClientEncryptions.create(encryptionSettings));
}
}
}

View File

@@ -15,108 +15,16 @@
*/
package org.springframework.data.mongodb.core.encryption;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.Map;
import org.bson.BsonBinary;
import org.bson.Document;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.convert.PropertyValueConverterFactory;
import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions.MongoConverterConfigurationAdapter;
import org.springframework.data.mongodb.core.convert.encryption.MongoEncryptionConverter;
import org.springframework.data.mongodb.core.encryption.EncryptionTests.Config;
import org.springframework.data.util.Lazy;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import com.mongodb.ClientEncryptionSettings;
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoNamespace;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.IndexOptions;
import com.mongodb.client.model.Indexes;
import com.mongodb.client.model.vault.DataKeyOptions;
import com.mongodb.client.vault.ClientEncryptions;
/**
* @author Christoph Strobl
* @author Julia Lee
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = Config.class)
@ContextConfiguration(classes = AbstractEncryptionTestBase.EncryptionConfig.class)
public class EncryptionTests extends AbstractEncryptionTestBase {
@Configuration
static class Config extends AbstractMongoClientConfiguration {
@Autowired ApplicationContext applicationContext;
@Override
protected String getDatabaseName() {
return "fle-test";
}
@Bean
@Override
public MongoClient mongoClient() {
return super.mongoClient();
}
@Override
protected void configureConverters(MongoConverterConfigurationAdapter converterConfigurationAdapter) {
converterConfigurationAdapter
.registerPropertyValueConverterFactory(PropertyValueConverterFactory.beanFactoryAware(applicationContext));
}
@Bean
MongoEncryptionConverter encryptingConverter(MongoClientEncryption mongoClientEncryption) {
Lazy<BsonBinary> dataKey = Lazy.of(() -> mongoClientEncryption.getClientEncryption().createDataKey("local",
new DataKeyOptions().keyAltNames(Collections.singletonList("mySuperSecretKey"))));
return new MongoEncryptionConverter(mongoClientEncryption,
EncryptionKeyResolver.annotated((ctx) -> EncryptionKey.keyId(dataKey.get())));
}
@Bean
CachingMongoClientEncryption clientEncryption(ClientEncryptionSettings encryptionSettings) {
return new CachingMongoClientEncryption(() -> ClientEncryptions.create(encryptionSettings));
}
@Bean
ClientEncryptionSettings encryptionSettings(MongoClient mongoClient) {
MongoNamespace keyVaultNamespace = new MongoNamespace("encryption.testKeyVault");
MongoCollection<Document> keyVaultCollection = mongoClient.getDatabase(keyVaultNamespace.getDatabaseName())
.getCollection(keyVaultNamespace.getCollectionName());
keyVaultCollection.drop();
// Ensure that two data keys cannot share the same keyAltName.
keyVaultCollection.createIndex(Indexes.ascending("keyAltNames"),
new IndexOptions().unique(true).partialFilterExpression(Filters.exists("keyAltNames")));
MongoCollection<Document> collection = mongoClient.getDatabase(getDatabaseName()).getCollection("test");
collection.drop(); // Clear old data
byte[] localMasterKey = new byte[96];
new SecureRandom().nextBytes(localMasterKey);
Map<String, Map<String, Object>> kmsProviders = Map.of("local", Map.of("key", localMasterKey));
// Create the ClientEncryption instance
return ClientEncryptionSettings.builder()
.keyVaultMongoClientSettings(
MongoClientSettings.builder().applyConnectionString(new ConnectionString("mongodb://localhost")).build()) //
.keyVaultNamespace(keyVaultNamespace.getFullName()) //
.kmsProviders(kmsProviders) //
.build();
}
}
}