Search code examples
amazon-web-servicesspring-bootspring-webfluxspring-data-mongodbaws-documentdb

Spring Data MongoDB: DBRef resolution is not supported


I have a Spring WebFlux App with mongo dB integration. It has Entities with @DBRef annotation. In order to @DBRef resolution, I have added both reactive and non-reactive dependencies for spring-data-mongodb as below.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

with above configs, @DBRef functionality works as expected even in my webflux application (spring-boot-starter-parent 2.4.4)

However, I have recently migrated my app to AWS and started to use AWS document db, for that, I had to create below seperate db configurations to establish reactive and none-reactive connectivities

@Slf4j
@Configuration
public class MongoConfiguration {

  private static final String SSL_CERTIFICATE = "sslCertificate";
  private static final String KEY_STORE_FILE_PREFIX = "sys-connect-via-ssl-test-cacerts";
  private static final String KEY_STORE_FILE_SUFFIX = ".jks";
  private static final String DEFAULT_KEY_STORE_KEYPW = "abcd";

  @Value("${mongodb.uri}")
  private String mongoURI;

  @Value("${default.dbname}")
  private String dbName;

  @Value("${default.certificateName}")
  private String defaultCertificatePath;

  /**
   * Reactive mongo database factory reactive mongo database factory.
   *
   * @return the reactive mongo database factory
   * @throws NoSuchAlgorithmException the no such algorithm exception
   * @throws CertificateException the certificate exception
   * @throws IOException the io exception
   * @throws KeyStoreException the key store exception
   * @throws KeyManagementException the key management exception
   */
  @Bean
  public ReactiveMongoDatabaseFactory reactiveMongoDatabaseFactory()
      throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException, KeyManagementException {

    MongoClientSettings.Builder builder = MongoClientSettings.builder();

    SslSettings.Builder sslBuilder = SslSettings.builder();

    X509Certificate caCert = createCertificate();
    File keyStoreFile = File.createTempFile(KEY_STORE_FILE_PREFIX, KEY_STORE_FILE_SUFFIX);
    FileOutputStream fos = new FileOutputStream(keyStoreFile.getPath());

    TrustManagerFactory trustManagerFactory = TrustManagerFactory
        .getInstance(TrustManagerFactory.getDefaultAlgorithm());
    KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    keyStore.load(null);
    keyStore.setCertificateEntry("caCert", caCert);
    keyStore.store(fos, DEFAULT_KEY_STORE_KEYPW.toCharArray());

    trustManagerFactory.init(keyStore);

    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
    sslBuilder.invalidHostNameAllowed(true);
    sslBuilder.enabled(true);

    sslBuilder.context(sslContext);
    SslSettings sslsettings = sslBuilder.build();

    builder.applyConnectionString(new ConnectionString(
        mongoURI));

    MongoClientSettings settings = builder
        .applyToSslSettings(builder1 -> builder1.applySettings(sslsettings)).build();

    return new SimpleReactiveMongoDatabaseFactory(MongoClients.create(settings), dbName);
  }

  @Bean
  public MongoDatabaseFactory mongoDatabaseFactory()
      throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException, KeyManagementException {

    MongoClientSettings.Builder builder = MongoClientSettings.builder();

    SslSettings.Builder sslBuilder = SslSettings.builder();

    X509Certificate caCert = createCertificate();
    File keyStoreFile = File.createTempFile(KEY_STORE_FILE_PREFIX, KEY_STORE_FILE_SUFFIX);
    FileOutputStream fos = new FileOutputStream(keyStoreFile.getPath());

    TrustManagerFactory trustManagerFactory = TrustManagerFactory
        .getInstance(TrustManagerFactory.getDefaultAlgorithm());
    KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    keyStore.load(null);
    keyStore.setCertificateEntry("caCert", caCert);
    keyStore.store(fos, DEFAULT_KEY_STORE_KEYPW.toCharArray());

    trustManagerFactory.init(keyStore);

    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
    sslBuilder.invalidHostNameAllowed(true);
    sslBuilder.enabled(true);

    sslBuilder.context(sslContext);
    SslSettings sslsettings = sslBuilder.build();

    builder.applyConnectionString(new ConnectionString(
        mongoURI));

    MongoClientSettings settings = builder
        .applyToSslSettings(builder1 -> builder1.applySettings(sslsettings)).build();

    return new SimpleMongoClientDatabaseFactory(com.mongodb.client.MongoClients.create(settings), dbName);
  }

  /**
   * Reactive mongo template reactive mongo template.
   *
   * @return the reactive mongo template
   * @throws CertificateException the certificate exception
   * @throws NoSuchAlgorithmException the no such algorithm exception
   * @throws IOException the io exception
   * @throws KeyStoreException the key store exception
   * @throws KeyManagementException the key management exception
   */
  @Bean
  public ReactiveMongoTemplate reactiveMongoTemplate()
      throws CertificateException, NoSuchAlgorithmException, IOException, KeyStoreException, KeyManagementException {
    return new ReactiveMongoTemplate(reactiveMongoDatabaseFactory());
  }

  @Primary
  @Bean
  public MongoTemplate mongoTemplate()
      throws CertificateException, NoSuchAlgorithmException, IOException, KeyStoreException, KeyManagementException {
    return new MongoTemplate(mongoDatabaseFactory());
  }

  private X509Certificate createCertificate()
      throws CertificateException, MalformedURLException {
    String sslCertificate = System.getProperty(SSL_CERTIFICATE);
    if (StringUtils.isEmpty(sslCertificate)) {
      sslCertificate = defaultCertificatePath;
    }

    CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
    URL url = new File(sslCertificate).toURI().toURL();

    try (InputStream certInputStream = url.openStream()) {
      return (X509Certificate) certFactory.generateCertificate(certInputStream);
    } catch (IOException e) {
      log.error(e.getMessage());
      return null;
    }
  }

}

with above settings, my application return UnsupportedOperationException DBRef resolution is not supported! error when involving entities with @DBRef annotation.

Please note that before AWS migration, no DB configuration beans were manually implemented. Just added two dependencies which mentioned earlier along with value for spring.data.mongodb.uri property.


Solution

  • DBRef is a feature that is not currently supported with Amazon DocumentDB.