Merge pull request #1466 from rajuan/MIFOSX-2153

MIFOSX-2153:Added support to use seperate DB server instance for reports
This commit is contained in:
Vishwas Babu A J
2015-08-24 17:40:25 +05:30
14 changed files with 457 additions and 233 deletions
@@ -74,8 +74,8 @@ public class PortfolioCommandSourceWritePlatformServiceImpl implements Portfolio
CommandProcessingResult result = null;
JsonCommand command = null;
Integer numberOfRetries = 0;
Integer maxNumberOfRetries = ThreadLocalContextUtil.getTenant().getMaxRetriesOnDeadlock();
Integer maxIntervalBetweenRetries = ThreadLocalContextUtil.getTenant().getMaxIntervalBetweenRetries();
Integer maxNumberOfRetries = ThreadLocalContextUtil.getTenant().getConnection().getMaxRetriesOnDeadlock();
Integer maxIntervalBetweenRetries = ThreadLocalContextUtil.getTenant().getConnection().getMaxIntervalBetweenRetries();
final JsonElement parsedCommand = this.fromApiJsonHelper.parse(json);
command = JsonCommand.from(json, parsedCommand, this.fromApiJsonHelper, wrapper.getEntityName(), wrapper.getEntityId(),
wrapper.getSubentityId(), wrapper.getGroupId(), wrapper.getClientId(), wrapper.getLoanId(), wrapper.getSavingsId(),
@@ -60,23 +60,23 @@ public class TenantDataSourcePortFixService {
public void fixUpTenantsSchemaServerPort() {
if (!enabled) {
logger.info("No schema_server_port UPDATE made to tenants table of the mifosplatform-tenants schema, because " + ENABLED + " = false");
logger.info("No schema_server_port UPDATE made to tenant_server_connections table of the mifosplatform-tenants schema, because " + ENABLED + " = false");
return;
}
if (dsp == null) {
// we don't have any generic mechanism to know the DB port, given just a tenant DataSource
logger.debug("No schema_server_port UPDATE made to tenants table of the mifosplatform-tenants schema (because neither MariaDB4j nor our own Spring Boot DataSourceConfiguration is used in a traditional WAR)");
logger.debug("No schema_server_port UPDATE made to tenant_server_connections table of the mifosplatform-tenants schema (because neither MariaDB4j nor our own Spring Boot DataSourceConfiguration is used in a traditional WAR)");
return;
}
int r = jdbcTemplate
.update("UPDATE tenants SET schema_server = ?, schema_server_port = ?, schema_username = ?, schema_password = ?",
.update("UPDATE tenant_server_connections SET schema_server = ?, schema_server_port = ?, schema_username = ?, schema_password = ?",
dsp.getHost(), dsp.getPort(), dsp.getUsername(), dsp.getPassword());
if ( r == 0 )
logger.warn("UPDATE tenants SET ... did not update ANY rows - something is probably wrong");
logger.warn("UPDATE tenant_server_connections SET ... did not update ANY rows - something is probably wrong");
else
logger.info("Upated "
+ r
+ " rows in the tenants table of the mifosplatform-tenants schema to the real current host: "
+ " rows in the tenant_server_connections table of the mifosplatform-tenants schema to the real current host: "
+ dsp.getHost() + ", port: " + dsp.getPort());
}
@@ -10,115 +10,16 @@ public class MifosPlatformTenant {
private final Long id;
private final String tenantIdentifier;
private final String name;
private final String schemaName;
private final String schemaServer;
private final String schemaServerPort;
private final String schemaUsername;
private final String schemaPassword;
private final String timezoneId;
private final boolean autoUpdateEnabled;
private final int initialSize;
private final boolean testOnBorrow;
private final long validationInterval;
private final boolean removeAbandoned;
private final int removeAbandonedTimeout;
private final boolean logAbandoned;
private final int abandonWhenPercentageFull;
private final int maxActive;
private final int minIdle;
private final int maxIdle;
private final int suspectTimeout;
private final int timeBetweenEvictionRunsMillis;
private final int minEvictableIdleTimeMillis;
private final int maxRetriesOnDeadlock;
private final int maxIntervalBetweenRetries;
private final MifosPlatformTenantConnection connection;
public MifosPlatformTenant(final Long id, final String tenantIdentifier, final String name, final String schemaName,
final String schemaServer, final String schemaServerPort, final String schemaUsername, final String schemaPassword,
final String timezoneId, final boolean autoUpdateEnabled, final int initialSize, final boolean testOnBorrow,
final long validationInterval, final boolean removeAbandoned, final int removeAbandonedTimeout, final boolean logAbandoned,
final int abandonWhenPercentageFull, final int maxActive, final int minIdle, final int maxIdle, final int suspectTimeout,
final int timeBetweenEvictionRunsMillis, final int minEvictableIdleTimeMillis, final int maxRetriesOnDeadlock,
final int maxIntervalBetweenRetries) {
public MifosPlatformTenant(final Long id, final String tenantIdentifier, final String name,
final String timezoneId, final MifosPlatformTenantConnection connection) {
this.id = id;
this.tenantIdentifier = tenantIdentifier;
this.name = name;
this.schemaName = schemaName;
this.schemaServer = schemaServer;
this.schemaServerPort = schemaServerPort;
this.schemaUsername = schemaUsername;
this.schemaPassword = schemaPassword;
this.timezoneId = timezoneId;
this.autoUpdateEnabled = autoUpdateEnabled;
this.initialSize = initialSize;
this.testOnBorrow = testOnBorrow;
this.validationInterval = validationInterval;
this.removeAbandoned = removeAbandoned;
this.removeAbandonedTimeout = removeAbandonedTimeout;
this.logAbandoned = logAbandoned;
this.abandonWhenPercentageFull = abandonWhenPercentageFull;
this.maxActive = maxActive;
this.minIdle = minIdle;
this.maxIdle = maxIdle;
this.suspectTimeout = suspectTimeout;
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
this.maxIntervalBetweenRetries = maxIntervalBetweenRetries;
this.maxRetriesOnDeadlock = maxRetriesOnDeadlock;
}
public String databaseURL() {
final String url = new StringBuilder("jdbc:mysql://").append(this.schemaServer).append(':').append(this.schemaServerPort)
.append('/').append(this.schemaName).toString();
return url;
}
public int getMaxActive() {
return this.maxActive;
}
public int getMinIdle() {
return this.minIdle;
}
public int getMaxIdle() {
return this.maxIdle;
}
public int getTimeBetweenEvictionRunsMillis() {
return this.timeBetweenEvictionRunsMillis;
}
public int getMinEvictableIdleTimeMillis() {
return this.minEvictableIdleTimeMillis;
}
public int getInitialSize() {
return initialSize;
}
public boolean isTestOnBorrow() {
return testOnBorrow;
}
public long getValidationInterval() {
return validationInterval;
}
public boolean isRemoveAbandoned() {
return removeAbandoned;
}
public int getRemoveAbandonedTimeout() {
return removeAbandonedTimeout;
}
public boolean isLogAbandoned() {
return logAbandoned;
}
public int getAbandonWhenPercentageFull() {
return abandonWhenPercentageFull;
this.connection = connection;
}
public Long getId() {
@@ -133,36 +34,12 @@ public class MifosPlatformTenant {
return this.name;
}
public String getSchemaName() {
return this.schemaName;
}
public String getSchemaUsername() {
return this.schemaUsername;
}
public String getSchemaPassword() {
return this.schemaPassword;
}
public String getTimezoneId() {
return this.timezoneId;
}
public boolean isAutoUpdateEnabled() {
return this.autoUpdateEnabled;
}
public int getSuspectTimeout() {
return this.suspectTimeout;
}
public int getMaxRetriesOnDeadlock() {
return maxRetriesOnDeadlock;
}
public int getMaxIntervalBetweenRetries() {
return maxIntervalBetweenRetries;
public MifosPlatformTenantConnection getConnection() {
return connection;
}
}
@@ -0,0 +1,242 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/**
*
*/
package org.mifosplatform.infrastructure.core.domain;
/**
* Holds DB server connection details.
*
*/
public class MifosPlatformTenantConnection {
private final Long connectionId;
private final String schemaServer;
private final String schemaServerPort;
private final String schemaUsername;
private final String schemaPassword;
private final String schemaName;
private final boolean autoUpdateEnabled;
private final int initialSize;
private final long validationInterval;
private final boolean removeAbandoned;
private final int removeAbandonedTimeout;
private final boolean logAbandoned;
private final int abandonWhenPercentageFull;
private final int maxActive;
private final int minIdle;
private final int maxIdle;
private final int suspectTimeout;
private final int timeBetweenEvictionRunsMillis;
private final int minEvictableIdleTimeMillis;
private final int maxRetriesOnDeadlock;
private final int maxIntervalBetweenRetries;
private final boolean testOnBorrow;
public MifosPlatformTenantConnection(final Long connectionId,final String schemaName, String schemaServer,final String schemaServerPort,final String schemaUsername,final String schemaPassword,
final boolean autoUpdateEnabled,final int initialSize,final long validationInterval,final boolean removeAbandoned,final int removeAbandonedTimeout,
final boolean logAbandoned,final int abandonWhenPercentageFull,final int maxActive,final int minIdle,final int maxIdle,final int suspectTimeout,
final int timeBetweenEvictionRunsMillis,final int minEvictableIdleTimeMillis,final int maxRetriesOnDeadlock,final int maxIntervalBetweenRetries,final boolean tesOnBorrow) {
this.connectionId = connectionId;
this.schemaName =schemaName;
this.schemaServer = schemaServer;
this.schemaServerPort = schemaServerPort;
this.schemaUsername = schemaUsername;
this.schemaPassword = schemaPassword;
this.autoUpdateEnabled = autoUpdateEnabled;
this.initialSize = initialSize;
this.validationInterval = validationInterval;
this.removeAbandoned = removeAbandoned;
this.removeAbandonedTimeout = removeAbandonedTimeout;
this.logAbandoned = logAbandoned;
this.abandonWhenPercentageFull = abandonWhenPercentageFull;
this.maxActive = maxActive;
this.minIdle = minIdle;
this.maxIdle = maxIdle;
this.suspectTimeout = suspectTimeout;
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
this.maxRetriesOnDeadlock = maxRetriesOnDeadlock;
this.maxIntervalBetweenRetries = maxIntervalBetweenRetries;
this.testOnBorrow=tesOnBorrow;
}
public String databaseURL() {
final String url = new StringBuilder("jdbc:mysql://").append(this.schemaServer).append(':').append(this.schemaServerPort)
.append('/').append(this.schemaName).toString();
return url;
}
/**
* @return the schemaServer
*/
public String getSchemaServer() {
return this.schemaServer;
}
/**
* @return the schemaServerPort
*/
public String getSchemaServerPort() {
return this.schemaServerPort;
}
/**
* @return the schemaUsername
*/
public String getSchemaUsername() {
return this.schemaUsername;
}
/**
* @return the schemaPassword
*/
public String getSchemaPassword() {
return this.schemaPassword;
}
/**
* @return the autoUpdateEnabled
*/
public boolean isAutoUpdateEnabled() {
return this.autoUpdateEnabled;
}
/**
* @return the initialSize
*/
public int getInitialSize() {
return this.initialSize;
}
/**
* @return the validationInterval
*/
public long getValidationInterval() {
return this.validationInterval;
}
/**
* @return the removeAbandoned
*/
public boolean isRemoveAbandoned() {
return this.removeAbandoned;
}
/**
* @return the removeAbandonedTimeout
*/
public int getRemoveAbandonedTimeout() {
return this.removeAbandonedTimeout;
}
/**
* @return the logAbandoned
*/
public boolean isLogAbandoned() {
return this.logAbandoned;
}
/**
* @return the abandonWhenPercentageFull
*/
public int getAbandonWhenPercentageFull() {
return this.abandonWhenPercentageFull;
}
/**
* @return the maxActive
*/
public int getMaxActive() {
return this.maxActive;
}
/**
* @return the minIdle
*/
public int getMinIdle() {
return this.minIdle;
}
/**
* @return the maxIdle
*/
public int getMaxIdle() {
return this.maxIdle;
}
/**
* @return the suspectTimeout
*/
public int getSuspectTimeout() {
return this.suspectTimeout;
}
/**
* @return the timeBetweenEvictionRunsMillis
*/
public int getTimeBetweenEvictionRunsMillis() {
return this.timeBetweenEvictionRunsMillis;
}
/**
* @return the minEvictableIdleTimeMillis
*/
public int getMinEvictableIdleTimeMillis() {
return this.minEvictableIdleTimeMillis;
}
/**
* @return the maxRetriesOnDeadlock
*/
public int getMaxRetriesOnDeadlock() {
return this.maxRetriesOnDeadlock;
}
/**
* @return the maxIntervalBetweenRetries
*/
public int getMaxIntervalBetweenRetries() {
return this.maxIntervalBetweenRetries;
}
public boolean isTestOnBorrow() {
return testOnBorrow;
}
public Long getConnectionId() {
return connectionId;
}
public String getSchemaName() {
return schemaName;
}
@Override
public String toString() {
return this.schemaName+":"+this.schemaServer+":"+this.schemaServerPort;
}
}
@@ -12,6 +12,7 @@ import javax.sql.DataSource;
import org.mifosplatform.infrastructure.core.boot.db.TenantDataSourcePortFixService;
import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenant;
import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenantConnection;
import org.mifosplatform.infrastructure.security.service.TenantDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -32,11 +33,8 @@ public class TenantDatabaseUpgradeService {
protected final TenantDataSourcePortFixService tenantDataSourcePortFixService;
@Autowired
public TenantDatabaseUpgradeService(
final TenantDetailsService detailsService,
@Qualifier("tenantDataSourceJndi") final DataSource dataSource,
TenantDataSourcePortFixService tenantDataSourcePortFixService)
{
public TenantDatabaseUpgradeService(final TenantDetailsService detailsService,
@Qualifier("tenantDataSourceJndi") final DataSource dataSource, TenantDataSourcePortFixService tenantDataSourcePortFixService) {
this.tenantDetailsService = detailsService;
this.tenantDataSource = dataSource;
this.tenantDataSourcePortFixService = tenantDataSourcePortFixService;
@@ -44,36 +42,37 @@ public class TenantDatabaseUpgradeService {
@PostConstruct
public void upgradeAllTenants() {
upgradeTenantDB();
upgradeTenantDB();
final List<MifosPlatformTenant> tenants = this.tenantDetailsService.findAllTenants();
for (final MifosPlatformTenant tenant : tenants) {
if (tenant.isAutoUpdateEnabled()) {
final MifosPlatformTenantConnection connection = tenant.getConnection();
if (connection.isAutoUpdateEnabled()) {
final Flyway flyway = new Flyway();
flyway.setDataSource(tenant.databaseURL(), tenant.getSchemaUsername(), tenant.getSchemaPassword());
flyway.setDataSource(connection.databaseURL(), connection.getSchemaUsername(), connection.getSchemaPassword());
flyway.setLocations("sql/migrations/core_db");
flyway.setOutOfOrder(true);
try {
flyway.migrate();
flyway.migrate();
} catch (FlywayException e) {
String betterMessage = e.getMessage()
+ "; for Tenant DB URL: " + tenant.databaseURL()
+ ", username: " + tenant.getSchemaUsername();
throw new FlywayException(betterMessage, e.getCause());
String betterMessage = e.getMessage() + "; for Tenant DB URL: " + connection.databaseURL() + ", username: "
+ connection.getSchemaPassword();
throw new FlywayException(betterMessage, e.getCause());
}
}
}
}
/**
* Initializes, and if required upgrades (using Flyway) the Tenant DB itself.
*/
private void upgradeTenantDB() {
final Flyway flyway = new Flyway();
flyway.setDataSource(tenantDataSource);
flyway.setLocations("sql/migrations/list_db");
flyway.setOutOfOrder(true);
flyway.migrate();
/**
* Initializes, and if required upgrades (using Flyway) the Tenant DB
* itself.
*/
private void upgradeTenantDB() {
final Flyway flyway = new Flyway();
flyway.setDataSource(tenantDataSource);
flyway.setLocations("sql/migrations/list_db");
flyway.setOutOfOrder(true);
flyway.migrate();
tenantDataSourcePortFixService.fixUpTenantsSchemaServerPort();
}
tenantDataSourcePortFixService.fixUpTenantsSchemaServerPort();
}
}
@@ -20,7 +20,7 @@ public class ThreadLocalContextUtil {
private static final ThreadLocal<MifosPlatformTenant> tenantcontext = new ThreadLocal<>();
private static final ThreadLocal<String> authTokenContext = new ThreadLocal<>();
public static void setTenant(final MifosPlatformTenant tenant) {
Assert.notNull(tenant, "tenant cannot be null");
tenantcontext.set(tenant);
@@ -13,6 +13,7 @@ import javax.sql.DataSource;
import org.apache.tomcat.jdbc.pool.PoolConfiguration;
import org.apache.tomcat.jdbc.pool.PoolProperties;
import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenant;
import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenantConnection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@@ -42,17 +43,20 @@ public class TomcatJdbcDataSourcePerTenantService implements RoutingDataSourceSe
// default to tenant database datasource
DataSource tenantDataSource = this.tenantDataSource;
final MifosPlatformTenant tenant = ThreadLocalContextUtil.getTenant();
final MifosPlatformTenant tenant = ThreadLocalContextUtil.getTenant();
if (tenant != null) {
final MifosPlatformTenantConnection tenantConnection = tenant.getConnection();
synchronized (this.tenantToDataSourceMap) {
// if tenant information available switch to appropriate
// if tenantConnection information available switch to
// appropriate
// datasource
// for that tenant.
if (this.tenantToDataSourceMap.containsKey(tenant.getId())) {
tenantDataSource = this.tenantToDataSourceMap.get(tenant.getId());
if (this.tenantToDataSourceMap.containsKey(tenantConnection.getConnectionId())) {
tenantDataSource = this.tenantToDataSourceMap.get(tenantConnection.getConnectionId());
} else {
tenantDataSource = createNewDataSourceFor(tenant);
this.tenantToDataSourceMap.put(tenant.getId(), tenantDataSource);
tenantDataSource = createNewDataSourceFor(tenantConnection);
this.tenantToDataSourceMap.put(tenantConnection.getConnectionId(), tenantDataSource);
}
}
}
@@ -60,31 +64,31 @@ public class TomcatJdbcDataSourcePerTenantService implements RoutingDataSourceSe
return tenantDataSource;
}
private DataSource createNewDataSourceFor(final MifosPlatformTenant tenant) {
// creates the data source oltp and report databases
private DataSource createNewDataSourceFor(final MifosPlatformTenantConnection tenantConnectionObj) {
// see
// http://www.tomcatexpert.com/blog/2010/04/01/configuring-jdbc-pool-high-concurrency
// see also org.mifosplatform.DataSourceProperties.setMifosDefaults()
final String jdbcUrl = tenant.databaseURL();
// see also org.mifosplatform.DataSourceProperties.setMifosDefaults()
final String jdbcUrl = tenantConnectionObj.databaseURL();
final PoolConfiguration poolConfiguration = new PoolProperties();
poolConfiguration.setDriverClassName("com.mysql.jdbc.Driver");
poolConfiguration.setName(tenant.getSchemaName() + "_pool");
poolConfiguration.setName(tenantConnectionObj.getSchemaName() + "_pool");
poolConfiguration.setUrl(jdbcUrl);
poolConfiguration.setUsername(tenant.getSchemaUsername());
poolConfiguration.setPassword(tenant.getSchemaPassword());
poolConfiguration.setUsername(tenantConnectionObj.getSchemaUsername());
poolConfiguration.setPassword(tenantConnectionObj.getSchemaPassword());
poolConfiguration.setInitialSize(tenant.getInitialSize());
poolConfiguration.setInitialSize(tenantConnectionObj.getInitialSize());
poolConfiguration.setTestOnBorrow(tenant.isTestOnBorrow());
poolConfiguration.setTestOnBorrow(tenantConnectionObj.isTestOnBorrow());
poolConfiguration.setValidationQuery("SELECT 1");
poolConfiguration.setValidationInterval(tenant.getValidationInterval());
poolConfiguration.setValidationInterval(tenantConnectionObj.getValidationInterval());
poolConfiguration.setRemoveAbandoned(tenant.isRemoveAbandoned());
poolConfiguration.setRemoveAbandonedTimeout(tenant.getRemoveAbandonedTimeout());
poolConfiguration.setLogAbandoned(tenant.isLogAbandoned());
poolConfiguration.setAbandonWhenPercentageFull(tenant.getAbandonWhenPercentageFull());
poolConfiguration.setRemoveAbandoned(tenantConnectionObj.isRemoveAbandoned());
poolConfiguration.setRemoveAbandonedTimeout(tenantConnectionObj.getRemoveAbandonedTimeout());
poolConfiguration.setLogAbandoned(tenantConnectionObj.isLogAbandoned());
poolConfiguration.setAbandonWhenPercentageFull(tenantConnectionObj.getAbandonWhenPercentageFull());
/**
* Vishwas- Do we need to enable the below properties and add
@@ -30,6 +30,7 @@ import javax.ws.rs.core.StreamingOutput;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenant;
import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenantConnection;
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil;
@@ -248,9 +249,9 @@ public class ReadReportingServiceImpl implements ReadReportingService {
outputType = outputTypeParam;
}
if (!(outputType.equalsIgnoreCase("HTML") || outputType.equalsIgnoreCase("PDF") || outputType.equalsIgnoreCase("XLS") || outputType.equalsIgnoreCase("XLSX") || outputType
.equalsIgnoreCase("CSV"))) { throw new PlatformDataIntegrityException("error.msg.invalid.outputType",
"No matching Output Type: " + outputType); }
if (!(outputType.equalsIgnoreCase("HTML") || outputType.equalsIgnoreCase("PDF") || outputType.equalsIgnoreCase("XLS")
|| outputType.equalsIgnoreCase("XLSX") || outputType.equalsIgnoreCase("CSV"))) { throw new PlatformDataIntegrityException(
"error.msg.invalid.outputType", "No matching Output Type: " + outputType); }
if (this.noPentaho) { throw new PlatformDataIntegrityException("error.msg.no.pentaho", "Pentaho is not enabled",
"Pentaho is not enabled"); }
@@ -279,7 +280,7 @@ public class ReadReportingServiceImpl implements ReadReportingService {
PdfReportUtil.createPDF(masterReport, baos);
return Response.ok().entity(baos.toByteArray()).type("application/pdf").build();
}
if ("XLS".equalsIgnoreCase(outputType)) {
ExcelReportUtil.createXLS(masterReport, baos);
return Response.ok().entity(baos.toByteArray()).type("application/vnd.ms-excel")
@@ -367,16 +368,17 @@ public class ReadReportingServiceImpl implements ReadReportingService {
final String userhierarchy = currentUser.getOffice().getHierarchy();
logger.info("db URL:" + tenantUrl + " userhierarchy:" + userhierarchy);
rptParamValues.put("userhierarchy", userhierarchy);
final Long userid = currentUser.getId();
logger.info("db URL:" + tenantUrl + " userid:" + userid);
rptParamValues.put("userid", userid);
final MifosPlatformTenant tenant = ThreadLocalContextUtil.getTenant();
final MifosPlatformTenantConnection tenantConnection = tenant.getConnection();
rptParamValues.put("tenantUrl", tenantUrl);
rptParamValues.put("username", tenant.getSchemaUsername());
rptParamValues.put("password", tenant.getSchemaPassword());
rptParamValues.put("username", tenantConnection.getSchemaUsername());
rptParamValues.put("password", tenantConnection.getSchemaPassword());
} catch (final Exception e) {
logger.error("error.msg.reporting.error:" + e.getMessage());
throw new PlatformDataIntegrityException("error.msg.reporting.error", e.getMessage());
@@ -53,7 +53,7 @@ public class TenantAwareBasicAuthenticationFilter extends BasicAuthenticationFil
private static boolean firstRequestProcessed = false;
private final static Logger logger = LoggerFactory.getLogger(TenantAwareBasicAuthenticationFilter.class);
private final BasicAuthTenantDetailsService basicAuthTenantDetailsService;
private final ToApiJsonSerializer<PlatformRequestLog> toApiJsonSerializer;
private final ConfigurationDomainService configurationDomainService;
@@ -91,6 +91,7 @@ public class TenantAwareBasicAuthenticationFilter extends BasicAuthenticationFil
} else {
String tenantIdentifier = request.getHeader(this.tenantRequestHeader);
if (org.apache.commons.lang.StringUtils.isBlank(tenantIdentifier)) {
tenantIdentifier = request.getParameter("tenantIdentifier");
}
@@ -99,8 +100,12 @@ public class TenantAwareBasicAuthenticationFilter extends BasicAuthenticationFil
"No tenant identifier found: Add request header of '" + this.tenantRequestHeader
+ "' or add the parameter 'tenantIdentifier' to query string of request URL."); }
// check tenants database for tenantId
final MifosPlatformTenant tenant = this.basicAuthTenantDetailsService.loadTenantById(tenantIdentifier);
String pathInfo = request.getRequestURI();
boolean isReportRequest = false;
if (pathInfo != null && pathInfo.contains("report")) {
isReportRequest = true;
}
final MifosPlatformTenant tenant = this.basicAuthTenantDetailsService.loadTenantById(tenantIdentifier, isReportRequest);
ThreadLocalContextUtil.setTenant(tenant);
String authToken = request.getHeader("Authorization");
@@ -110,9 +115,9 @@ public class TenantAwareBasicAuthenticationFilter extends BasicAuthenticationFil
}
if (!firstRequestProcessed) {
final String baseUrl = request.getRequestURL().toString().replace(request.getPathInfo(), "/");
System.setProperty("baseUrl", baseUrl);
final String baseUrl = request.getRequestURL().toString().replace(request.getPathInfo(), "/");
System.setProperty("baseUrl", baseUrl);
final boolean ehcacheEnabled = this.configurationDomainService.isEhcacheEnabled();
if (ehcacheEnabled) {
this.cacheWritePlatformService.switchToCache(CacheType.SINGLE_NODE);
@@ -122,7 +127,7 @@ public class TenantAwareBasicAuthenticationFilter extends BasicAuthenticationFil
TenantAwareBasicAuthenticationFilter.firstRequestProcessed = true;
}
}
super.doFilter(req, res, chain);
} catch (final InvalidTenantIdentiferException e) {
// deal with exception at low level
@@ -9,5 +9,5 @@ import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenant;
public interface BasicAuthTenantDetailsService {
MifosPlatformTenant loadTenantById(String tenantId);
MifosPlatformTenant loadTenantById(String tenantId, boolean isReport);
}
@@ -11,6 +11,7 @@ import java.sql.SQLException;
import javax.sql.DataSource;
import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenant;
import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenantConnection;
import org.mifosplatform.infrastructure.security.exception.InvalidTenantIdentiferException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -36,34 +37,52 @@ public class BasicAuthTenantDetailsServiceJdbc implements BasicAuthTenantDetails
private static final class TenantMapper implements RowMapper<MifosPlatformTenant> {
private final StringBuilder sqlBuilder = new StringBuilder(
"id, name,identifier, schema_name as schemaName, schema_server as schemaServer, schema_server_port as schemaServerPort, auto_update as autoUpdate, ")//
.append(" schema_username as schemaUsername, schema_password as schemaPassword , timezone_id as timezoneId , pool_initial_size as initialSize, ") //
.append(" pool_validation_interval as validationInterval, pool_remove_abandoned as removeAbandoned, pool_remove_abandoned_timeout as removeAbandonedTimeout, ")//
.append(" pool_log_abandoned as logAbandoned, pool_abandon_when_percentage_full as abandonedWhenPercentageFull, pool_test_on_borrow as testOnBorrow, ")//
.append(" pool_max_active as poolMaxActive, pool_min_idle as poolMinIdle, pool_max_idle as poolMaxIdle, ")//
.append(" pool_suspect_timeout as poolSuspectTimeout, pool_time_between_eviction_runs_millis as poolTimeBetweenEvictionRunsMillis, ")//
.append(" pool_min_evictable_idle_time_millis as poolMinEvictableIdleTimeMillis, ")//
.append(" deadlock_max_retries as maxRetriesOnDeadlock, ")//
.append(" deadlock_max_retry_interval as maxIntervalBetweenRetries ")//
.append(" from tenants t");//
private final boolean isReport;
private final StringBuilder sqlBuilder = new StringBuilder(" t.id, ts.id as connectionId , ")//
.append(" t.timezone_id as timezoneId , t.name,t.identifier, ts.schema_name as schemaName, ts.schema_server as schemaServer,")//
.append(" ts.schema_server_port as schemaServerPort, ts.auto_update as autoUpdate,")//
.append(" ts.schema_username as schemaUsername, ts.schema_password as schemaPassword , ts.pool_initial_size as initialSize,")//
.append(" ts.pool_validation_interval as validationInterval, ts.pool_remove_abandoned as removeAbandoned, ts.pool_remove_abandoned_timeout as removeAbandonedTimeout,")//
.append(" ts.pool_log_abandoned as logAbandoned, ts.pool_abandon_when_percentage_full as abandonedWhenPercentageFull, ts.pool_test_on_borrow as testOnBorrow,")//
.append(" ts.pool_max_active as poolMaxActive, ts.pool_min_idle as poolMinIdle, ts.pool_max_idle as poolMaxIdle,")//
.append(" ts.pool_suspect_timeout as poolSuspectTimeout, ts.pool_time_between_eviction_runs_millis as poolTimeBetweenEvictionRunsMillis,")//
.append(" ts.pool_min_evictable_idle_time_millis as poolMinEvictableIdleTimeMillis,")//
.append(" ts.deadlock_max_retries as maxRetriesOnDeadlock,")//
.append(" ts.deadlock_max_retry_interval as maxIntervalBetweenRetries ")//
.append(" from tenants t left join tenant_server_connections ts ");
public TenantMapper(boolean isReport) {
this.isReport = isReport;
}
public String schema() {
if(this.isReport){
this.sqlBuilder.append(" on t.report_Id = ts.id");
}else{
this.sqlBuilder.append(" on t.oltp_Id = ts.id");
}
return this.sqlBuilder.toString();
}
@Override
public MifosPlatformTenant mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final String tenantIdentifier = rs.getString("identifier");
final String name = rs.getString("name");
final String timezoneId = rs.getString("timezoneId");
final MifosPlatformTenantConnection connection = getDBConnection(rs);
return new MifosPlatformTenant(id, tenantIdentifier, name, timezoneId, connection);
}
// gets the DB connection
private MifosPlatformTenantConnection getDBConnection(ResultSet rs) throws SQLException {
final Long connectionId = rs.getLong("connectionId");
final String schemaName = rs.getString("schemaName");
final String schemaServer = rs.getString("schemaServer");
final String schemaServerPort = rs.getString("schemaServerPort");
final String schemaUsername = rs.getString("schemaUsername");
final String schemaPassword = rs.getString("schemaPassword");
final String timezoneId = rs.getString("timezoneId");
final boolean autoUpdateEnabled = rs.getBoolean("autoUpdate");
final int initialSize = rs.getInt("initialSize");
final boolean testOnBorrow = rs.getBoolean("testOnBorrow");
@@ -84,10 +103,10 @@ public class BasicAuthTenantDetailsServiceJdbc implements BasicAuthTenantDetails
maxRetriesOnDeadlock = bindValueInMinMaxRange(maxRetriesOnDeadlock, 0, 15);
maxIntervalBetweenRetries = bindValueInMinMaxRange(maxIntervalBetweenRetries, 1, 15);
return new MifosPlatformTenant(id, tenantIdentifier, name, schemaName, schemaServer, schemaServerPort, schemaUsername,
schemaPassword, timezoneId, autoUpdateEnabled, initialSize, testOnBorrow, validationInterval, removeAbandoned,
removeAbandonedTimeout, logAbandoned, abandonWhenPercentageFull, maxActive, minIdle, maxIdle, suspectTimeout,
timeBetweenEvictionRunsMillis, minEvictableIdleTimeMillis, maxRetriesOnDeadlock, maxIntervalBetweenRetries);
return new MifosPlatformTenantConnection(connectionId, schemaName, schemaServer, schemaServerPort, schemaUsername,
schemaPassword, autoUpdateEnabled, initialSize, validationInterval, removeAbandoned, removeAbandonedTimeout,
logAbandoned, abandonWhenPercentageFull, maxActive, minIdle, maxIdle, suspectTimeout, timeBetweenEvictionRunsMillis,
minEvictableIdleTimeMillis, maxRetriesOnDeadlock, maxIntervalBetweenRetries, testOnBorrow);
}
private int bindValueInMinMaxRange(final int value, int min, int max) {
@@ -100,10 +119,10 @@ public class BasicAuthTenantDetailsServiceJdbc implements BasicAuthTenantDetails
@Override
@Cacheable(value = "tenantsById")
public MifosPlatformTenant loadTenantById(final String tenantIdentifier) {
public MifosPlatformTenant loadTenantById(final String tenantIdentifier, final boolean isReport) {
try {
final TenantMapper rm = new TenantMapper();
final TenantMapper rm = new TenantMapper(isReport);
final String sql = "select " + rm.schema() + " where t.identifier like ?";
return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { tenantIdentifier });
@@ -12,6 +12,7 @@ import java.util.List;
import javax.sql.DataSource;
import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenant;
import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenantConnection;
import org.mifosplatform.infrastructure.security.exception.InvalidTenantIdentiferException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -37,17 +38,18 @@ public class JdbcTenantDetailsService implements TenantDetailsService {
private static final class TenantMapper implements RowMapper<MifosPlatformTenant> {
private final StringBuilder sqlBuilder = new StringBuilder(
"id, name,identifier, schema_name as schemaName, schema_server as schemaServer, schema_server_port as schemaServerPort, auto_update as autoUpdate, ")//
.append(" schema_username as schemaUsername, schema_password as schemaPassword , timezone_id as timezoneId , pool_initial_size as initialSize, ") //
.append(" pool_validation_interval as validationInterval, pool_remove_abandoned as removeAbandoned, pool_remove_abandoned_timeout as removeAbandonedTimeout, ")//
.append(" pool_log_abandoned as logAbandoned, pool_abandon_when_percentage_full as abandonedWhenPercentageFull, pool_test_on_borrow as testOnBorrow, ")//
.append(" pool_max_active as poolMaxActive, pool_min_idle as poolMinIdle, pool_max_idle as poolMaxIdle, ")//
.append(" pool_suspect_timeout as poolSuspectTimeout, pool_time_between_eviction_runs_millis as poolTimeBetweenEvictionRunsMillis, ")//
.append(" pool_min_evictable_idle_time_millis as poolMinEvictableIdleTimeMillis, ")//
.append(" deadlock_max_retries as maxRetriesOnDeadlock, ")//
.append(" deadlock_max_retry_interval as maxIntervalBetweenRetries ")//
.append(" from tenants t");//
private final StringBuilder sqlBuilder = new StringBuilder("t.id, ts.id as connectionId , ")//
.append(" t.timezone_id as timezoneId , t.name,t.identifier, ts.schema_name as schemaName, ts.schema_server as schemaServer,")//
.append(" ts.schema_server_port as schemaServerPort, ts.auto_update as autoUpdate,")//
.append(" ts.schema_username as schemaUsername, ts.schema_password as schemaPassword , ts.pool_initial_size as initialSize,")//
.append(" ts.pool_validation_interval as validationInterval, ts.pool_remove_abandoned as removeAbandoned, ts.pool_remove_abandoned_timeout as removeAbandonedTimeout,")//
.append(" ts.pool_log_abandoned as logAbandoned, ts.pool_abandon_when_percentage_full as abandonedWhenPercentageFull, ts.pool_test_on_borrow as testOnBorrow,")//
.append(" ts.pool_max_active as poolMaxActive, ts.pool_min_idle as poolMinIdle, ts.pool_max_idle as poolMaxIdle,")//
.append(" ts.pool_suspect_timeout as poolSuspectTimeout, ts.pool_time_between_eviction_runs_millis as poolTimeBetweenEvictionRunsMillis,")//
.append(" ts.pool_min_evictable_idle_time_millis as poolMinEvictableIdleTimeMillis,")//
.append(" ts.deadlock_max_retries as maxRetriesOnDeadlock,")//
.append(" ts.deadlock_max_retry_interval as maxIntervalBetweenRetries ")//
.append(" from tenants t left join tenant_server_connections ts on t.oltp_Id=ts.id ");
public String schema() {
return this.sqlBuilder.toString();
@@ -55,16 +57,24 @@ public class JdbcTenantDetailsService implements TenantDetailsService {
@Override
public MifosPlatformTenant mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final String tenantIdentifier = rs.getString("identifier");
final String name = rs.getString("name");
final String timezoneId = rs.getString("timezoneId");
final MifosPlatformTenantConnection connection = getDBConnection(rs);
return new MifosPlatformTenant(id, tenantIdentifier, name, timezoneId, connection);
}
// gets the DB connection
private MifosPlatformTenantConnection getDBConnection(ResultSet rs) throws SQLException {
final Long connectionId = rs.getLong("connectionId");
final String schemaName = rs.getString("schemaName");
final String schemaServer = rs.getString("schemaServer");
final String schemaServerPort = rs.getString("schemaServerPort");
final String schemaUsername = rs.getString("schemaUsername");
final String schemaPassword = rs.getString("schemaPassword");
final String timezoneId = rs.getString("timezoneId");
final boolean autoUpdateEnabled = rs.getBoolean("autoUpdate");
final int initialSize = rs.getInt("initialSize");
final boolean testOnBorrow = rs.getBoolean("testOnBorrow");
@@ -85,10 +95,11 @@ public class JdbcTenantDetailsService implements TenantDetailsService {
maxRetriesOnDeadlock = bindValueInMinMaxRange(maxRetriesOnDeadlock, 0, 15);
maxIntervalBetweenRetries = bindValueInMinMaxRange(maxIntervalBetweenRetries, 1, 15);
return new MifosPlatformTenant(id, tenantIdentifier, name, schemaName, schemaServer, schemaServerPort, schemaUsername,
schemaPassword, timezoneId, autoUpdateEnabled, initialSize, testOnBorrow, validationInterval, removeAbandoned,
removeAbandonedTimeout, logAbandoned, abandonWhenPercentageFull, maxActive, minIdle, maxIdle, suspectTimeout,
timeBetweenEvictionRunsMillis, minEvictableIdleTimeMillis, maxRetriesOnDeadlock, maxIntervalBetweenRetries);
return new MifosPlatformTenantConnection(connectionId, schemaName, schemaServer, schemaServerPort, schemaUsername,
schemaPassword, autoUpdateEnabled, initialSize, validationInterval, removeAbandoned, removeAbandonedTimeout,
logAbandoned, abandonWhenPercentageFull, maxActive, minIdle, maxIdle, suspectTimeout, timeBetweenEvictionRunsMillis,
minEvictableIdleTimeMillis, maxRetriesOnDeadlock, maxIntervalBetweenRetries, testOnBorrow);
}
private int bindValueInMinMaxRange(final int value, int min, int max) {
@@ -34,12 +34,12 @@ import org.mifosplatform.infrastructure.core.exception.UnrecognizedQueryParamExc
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer;
import org.mifosplatform.infrastructure.core.service.Page;
import org.mifosplatform.infrastructure.core.service.SearchParameters;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.portfolio.accountdetails.data.AccountSummaryCollectionData;
import org.mifosplatform.portfolio.accountdetails.service.AccountDetailsReadPlatformService;
import org.mifosplatform.portfolio.client.data.ClientData;
import org.mifosplatform.portfolio.client.service.ClientReadPlatformService;
import org.mifosplatform.infrastructure.core.service.SearchParameters;
import org.mifosplatform.portfolio.savings.data.SavingsAccountData;
import org.mifosplatform.portfolio.savings.service.SavingsAccountReadPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -0,0 +1,65 @@
START TRANSACTION;
RENAME TABLE tenants to temp_tenants;
create table tenant_server_connections(`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`schema_server` VARCHAR(100) NOT NULL DEFAULT 'localhost',
`schema_name` VARCHAR(100) NOT NULL,
`schema_server_port` VARCHAR(10) NOT NULL DEFAULT '3306',
`schema_username` VARCHAR(100) NOT NULL DEFAULT 'root',
`schema_password` VARCHAR(100) NOT NULL DEFAULT 'mysql',
`auto_update` TINYINT(1) NOT NULL DEFAULT '1',
`pool_initial_size` INT(5) NULL DEFAULT '5',
`pool_validation_interval` INT(11) NULL DEFAULT '30000',
`pool_remove_abandoned` TINYINT(1) NULL DEFAULT '1',
`pool_remove_abandoned_timeout` INT(5) NULL DEFAULT '60',
`pool_log_abandoned` TINYINT(1) NULL DEFAULT '1',
`pool_abandon_when_percentage_full` INT(5) NULL DEFAULT '50',
`pool_test_on_borrow` TINYINT(1) NULL DEFAULT '1',
`pool_max_active` INT(5) NULL DEFAULT '40',
`pool_min_idle` INT(5) NULL DEFAULT '20',
`pool_max_idle` INT(5) NULL DEFAULT '10',
`pool_suspect_timeout` INT(5) NULL DEFAULT '60',
`pool_time_between_eviction_runs_millis` INT(11) NULL DEFAULT '34000',
`pool_min_evictable_idle_time_millis` INT(11) NULL DEFAULT '60000',
`deadlock_max_retries` INT(5) NULL DEFAULT '0',
`deadlock_max_retry_interval` INT(5) NULL DEFAULT '1',
PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=2;
INSERT INTO `tenant_server_connections` (`id`,`schema_name`,`schema_server`, `schema_server_port`, `schema_username`, `schema_password`, `auto_update`, `pool_initial_size`, `pool_validation_interval`, `pool_remove_abandoned`, `pool_remove_abandoned_timeout`, `pool_log_abandoned`, `pool_abandon_when_percentage_full`, `pool_test_on_borrow`, `pool_max_active`, `pool_min_idle`, `pool_max_idle`, `pool_suspect_timeout`, `pool_time_between_eviction_runs_millis`, `pool_min_evictable_idle_time_millis`, `deadlock_max_retries`, `deadlock_max_retry_interval`)
SELECT `id`,`schema_name`,`schema_server`, `schema_server_port`, `schema_username`, `schema_password`, `auto_update`, `pool_initial_size`, `pool_validation_interval`, `pool_remove_abandoned`, `pool_remove_abandoned_timeout`, `pool_log_abandoned`, `pool_abandon_when_percentage_full`, `pool_test_on_borrow`, `pool_max_active`, `pool_min_idle`, `pool_max_idle`, `pool_suspect_timeout`, `pool_time_between_eviction_runs_millis`, `pool_min_evictable_idle_time_millis`, `deadlock_max_retries`, `deadlock_max_retry_interval` from temp_tenants;
CREATE TABLE tenants(
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`identifier` VARCHAR(100) NOT NULL,
`name` VARCHAR(100) NOT NULL,
`timezone_id` VARCHAR(100) NOT NULL,
`country_id` INT(11) NULL DEFAULT NULL,
`joined_date` DATE NULL DEFAULT NULL,
`created_date` DATETIME NULL DEFAULT NULL,
`lastmodified_date` DATETIME NULL DEFAULT NULL,
`oltp_id` BIGINT(20) NOT NULL,
`report_id` BIGINT(20) NOT NULL,
PRIMARY KEY (`id`),
INDEX `fk_oltp_id` (`oltp_id`),
INDEX `fk_report_id` (`report_id`),
CONSTRAINT `fk_oltp_id` FOREIGN KEY (`oltp_id`) REFERENCES `tenant_server_connections` (`id`),
CONSTRAINT `fk_report_id` FOREIGN KEY (`report_id`) REFERENCES `tenant_server_connections` (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=6;
INSERT INTO tenants(`id`,`identifier`,`name`,`timezone_id`,`country_id`,`joined_date`,`created_date`,`lastmodified_date`,`oltp_id`, `report_id`)
SELECT `id`,`identifier`,`name`,`timezone_id`,`country_id`,`joined_date`,`created_date`,`lastmodified_date`,`id`, `id` from temp_tenants ;
DROP TABLE temp_tenants;
COMMIT;