5

I have a situation where I have multiple clients connecting to my application and I want to give each one their own "schema/database" in the same Mongo server.

My configuration class:

@Configuration
public class MongoDbConfiguration {

@Bean
@Primary
public MongoDbFactory mongoDbFactory() throws UnknownHostException {
    return new MultiTenantMongoDbFactory();
}

@Bean
@Primary
public MongoTemplate mongoTemplate() throws UnknownHostException {
    return new MongoTemplate(mongoDbFactory());
}
}

Multitenant Db Factory

public class MultiTenantMongoDbFactory extends SimpleMongoDbFactory {

public MultiTenantMongoDbFactory() throws UnknownHostException {
    super(getMongoClient(), TenantContext.getTenant());
}

@Override
public DB getDb() throws DataAccessException {
    String tenant = TenantContext.getTenant();
    return getDb(tenant);

}

private static MongoClient getMongoClient() {
    String tenant = TenantContext.getTenant();
    System.out.println("Database name in factory class :"+tenant);
    if (tenant.equalsIgnoreCase("ncet")) {
        MongoCredential mongoCredential = MongoCredential.createCredential("user1", "db1",
                "pwd1".toCharArray());
        ServerAddress serverAddress = new ServerAddress("localhost", 27017);
        MongoClient mongoClient = new MongoClient(serverAddress, Arrays.asList(mongoCredential));
        return mongoClient;
    }else{
        MongoCredential mongoCredential = MongoCredential.createCredential("user1", "db2",
                "pwd2".toCharArray());
        ServerAddress serverAddress = new ServerAddress("localhost", 27017);
        MongoClient mongoClient = new MongoClient(serverAddress, Arrays.asList(mongoCredential));
        return mongoClient;
    }

}

Each database has credentials

4
  • Your problem is not clear. Is this authentication issue or mongodb with multiple dbs issue ? Are you trying to configure a replica set ? If you have an error, could you please provide the stacktrace ? Commented Oct 19, 2017 at 10:44
  • I am using spring-data mongo repositories and spring boot in my application. For a single mongo instance with multiple databases needs to configure. application has multiple clients and each client has dedicated database. trying do multi tency Commented Oct 19, 2017 at 10:51
  • What is the exception you are getting, post the stack trace in your question Commented Oct 19, 2017 at 11:45
  • It is injecting only first db, and when i try to connect with second tenant (different db) it giving authorization exception. credential are correct. it is letting me to work with single database. Commented Oct 19, 2017 at 12:01

1 Answer 1

6
+50

Your sample doesn't work because getMongoClient() invokes only once during startup, but you need to change it in runtime based on active tenant. It's pretty straightforward to implement dedicated MongoDbFactory for multi-tenancy based on spring SimpleMongoDbFactory as example. You can add more logic to it if needed (for writeConcern, etc). There are two tenants (east and west) in this sample. Each tenant has its own MongoClient with corresponding database name and credentials configured in MongoConfig. TenantDataFactory returns tenant related information based on current Tenant in TenantContext. DB object is created using MongoClient and database name from TenantData returned by TenantDataFactory.

public class MultiTenantMongoDbFactory implements MongoDbFactory {

  private PersistenceExceptionTranslator exceptionTranslator;
  private TenantDataFactory tenantDataFactory;

  public MultiTenantMongoDbFactory(TenantDataFactory tenantDataFactory) {
    this.exceptionTranslator = new MongoExceptionTranslator();
    this.tenantDataFactory = tenantDataFactory;
  }

  @Override
  public DB getDb(String dbName) throws DataAccessException {
    return getDb();
  }

  @Override
  public DB getDb() throws DataAccessException {
    Tenant tenant = TenantContext.getCurrentTenant();
    TenantData tenantData = tenantDataFactory.getTenantData(tenant);
    return MongoDbUtils.getDB(tenantData.getClient(), tenantData.getDbName());
  }

  @Override
  public PersistenceExceptionTranslator getExceptionTranslator() {
    return exceptionTranslator;
  }
}

public class TenantDataFactory {

  private Map<Tenant, TenantData> tenantDataMap;

  public TenantDataFactory(Map<Tenant, TenantData> tenantDataMap) {
    this.tenantDataMap = Collections.unmodifiableMap(tenantDataMap);
  }

  public TenantData getTenantData(Tenant tenant) {
    TenantData tenantData = tenantDataMap.get(tenant);
    if (tenantData == null) {
      // or return default tenant
      throw new IllegalArgumentException("Unsupported tenant " + tenant);
    }
    return tenantData;
  }
}

public enum Tenant {
  EAST, WEST
}

public class TenantData {

  private final String dbName;
  private final MongoClient client;

  public TenantData(String dbName, MongoClient client) {
    this.dbName = dbName;
    this.client = client;
  }

  public String getDbName() {
    return dbName;
  }

  public MongoClient getClient() {
    return client;
  }
}

public class TenantContext {

  private static ThreadLocal<Tenant> currentTenant = new ThreadLocal<>();

  public static void setCurrentTenant(Tenant tenant) {
    currentTenant.set(tenant);
  }

  public static Tenant getCurrentTenant() {
    return currentTenant.get();
  }
}

@Configuration
public class MongoConfig {

  @Bean(name = "eastMongoClient", destroyMethod = "close")
  public MongoClient eastMongoClient() {
    return new MongoClient(new ServerAddress("127.0.0.1", 27017),
        Collections.singletonList(MongoCredential.createCredential("user1", "east", "password1".toCharArray())));
  }

  @Bean(name = "westMongoClient", destroyMethod = "close")
  public MongoClient westMongoClient() {
    return new MongoClient(new ServerAddress("127.0.0.1", 27017),
        Collections.singletonList(MongoCredential.createCredential("user2", "west", "password2".toCharArray())));
  }

  @Bean
  public TenantDataFactory tenantDataFactory(@Qualifier("eastMongoClient") MongoClient eastMongoClient,
                                             @Qualifier("westMongoClient") MongoClient westMongoClient) {
    Map<Tenant, TenantData> tenantDataMap = new HashMap<>();
    tenantDataMap.put(Tenant.EAST, new TenantData("east", eastMongoClient));
    tenantDataMap.put(Tenant.WEST, new TenantData("west", westMongoClient));
    return new TenantDataFactory(tenantDataMap);
  }

  @Bean
  public MongoDbFactory mongoDbFactory(@Autowired TenantDataFactory tenantDataFactory) {
    return new MultiTenantMongoDbFactory(tenantDataFactory);
  }

  @Bean
  public MongoTemplate mongoTemplate(@Autowired MongoDbFactory mongoDbFactory) {
    return new MongoTemplate(mongoDbFactory);
  }
}
Sign up to request clarification or add additional context in comments.

11 Comments

Each instance has credential how to pass credentials. the above approach how to send credentials? please help me to under stand. I solved by using :[email protected]/atanvi/multitency.git not sure about the approach ...
There are two separate MongoClient for each tenant with distinct hosts and credentials in MongoConfig.
There should be only one MongoClient for each tenant, I not sure how to do it
i added more details in original answer
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.