Added multi tenant catalog support. (#987)

This commit is contained in:
Alexander Winkler
2017-04-01 00:41:08 +13:00
committed by Rob Bygrave
parent 0557e6cd08
commit 485e62b5d6
6 changed files with 195 additions and 6 deletions
@@ -129,6 +129,8 @@ public class ServerConfig {
private TenantSchemaProvider tenantSchemaProvider;
private TenantCatalogProvider tenantCatalogProvider;
/**
* List of interesting classes such as entities, embedded, ScalarTypes,
* Listeners, Finders, Controllers etc.
@@ -696,6 +698,20 @@ public class ServerConfig {
this.tenantSchemaProvider = tenantSchemaProvider;
}
/**
* Return the tenancy catalog provider.
*/
public TenantCatalogProvider getTenantCatalogProvider() {
return tenantCatalogProvider;
}
/**
* Set the tenancy catalog provider.
*/
public void setTenantCatalogProvider(TenantCatalogProvider tenantCatalogProvider) {
this.tenantCatalogProvider = tenantCatalogProvider;
}
/**
* Return the PersistBatch mode to use by default at the transaction level.
* <p>
@@ -0,0 +1,16 @@
package io.ebean.config;
/**
* For multi-tenancy via DB CATALOG supply the catalog given the tenantId.
*/
@FunctionalInterface
public interface TenantCatalogProvider {
/**
* Return the DB catalog for the given tenantId.
*
* @param tenantId The current tenant id.
* @return The DB catalog to use for the given tenant
*/
String catalog(Object tenantId);
}
@@ -20,6 +20,11 @@ public enum TenantMode {
*/
SCHEMA(true),
/**
* Each Tenant has their own Database but with in connection pool
*/
CATALOG(true),
/**
* Tenants share tables but have a discriminator/partition column that partitions the data.
*/
@@ -371,6 +371,8 @@ public class InternalConfiguration {
return new MultiTenantDbSupplier(serverConfig.getCurrentTenantProvider(), serverConfig.getTenantDataSourceProvider());
case SCHEMA:
return new MultiTenantDbSchemaSupplier(serverConfig.getCurrentTenantProvider(), serverConfig.getDataSource(), serverConfig.getTenantSchemaProvider());
case CATALOG:
return new MultiTenantDbCatalogSupplier(serverConfig.getCurrentTenantProvider(), serverConfig.getDataSource(), serverConfig.getTenantCatalogProvider());
default:
return new SimpleDataSourceProvider(serverConfig.getDataSource());
}
@@ -0,0 +1,125 @@
package io.ebeaninternal.server.core;
import io.ebean.config.CurrentTenantProvider;
import io.ebean.config.TenantCatalogProvider;
import io.ebeaninternal.server.transaction.DataSourceSupplier;
import org.avaje.datasource.DataSourcePool;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
/**
* DataSource supplier that changes DB catalog based on current Tenant Id.
*/
public class MultiTenantDbCatalogSupplier implements DataSourceSupplier {
private final CurrentTenantProvider tenantProvider;
private final DataSource dataSource;
private final TenantCatalogProvider catalogProvider;
private final CatalogDataSource catalogDataSource;
MultiTenantDbCatalogSupplier(CurrentTenantProvider tenantProvider, DataSource dataSource, TenantCatalogProvider catalogProvider) {
this.tenantProvider = tenantProvider;
this.dataSource = dataSource;
this.catalogProvider = catalogProvider;
this.catalogDataSource = new CatalogDataSource();
}
@Override
public DataSource getDataSource() {
return catalogDataSource;
}
@Override
public Connection getConnection(Object tenantId) throws SQLException {
return catalogDataSource.getConnectionForTenant(tenantId);
}
@Override
public void shutdown(boolean deregisterDriver) {
if (dataSource instanceof DataSourcePool) {
((DataSourcePool) dataSource).shutdown(deregisterDriver);
}
}
/**
* Returns the DB catalog for the current user Tenant Id.
*/
private String tenantCatalog() {
return catalogProvider.catalog(tenantProvider.currentId());
}
private class CatalogDataSource implements DataSource {
CatalogDataSource() {
}
/**
* Return the connection where tenantId is optionally provided by a lazy loading query.
*/
Connection getConnectionForTenant(Object tenantId) throws SQLException {
Connection connection = dataSource.getConnection();
connection.setCatalog(catalogProvider.catalog(tenantId));
return connection;
}
/**
* Return the connection with the appropriate DB catalog set.
*/
@Override
public Connection getConnection() throws SQLException {
Connection connection = dataSource.getConnection();
connection.setCatalog(tenantCatalog());
return connection;
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return dataSource.getConnection(username, password);
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return dataSource.unwrap(iface);
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return dataSource.isWrapperFor(iface);
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return dataSource.getLogWriter();
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
dataSource.setLogWriter(out);
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
dataSource.setLoginTimeout(seconds);
}
@Override
public int getLoginTimeout() throws SQLException {
return dataSource.getLoginTimeout();
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return dataSource.getParentLogger();
}
}
}