diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/cache/PlatformCacheConfiguration.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/cache/PlatformCacheConfiguration.java new file mode 100644 index 000000000..e2f6c555d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/cache/PlatformCacheConfiguration.java @@ -0,0 +1,29 @@ +package org.mifosplatform.infrastructure.cache; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.CachingConfigurer; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.interceptor.DefaultKeyGenerator; +import org.springframework.cache.interceptor.KeyGenerator; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableCaching +public class PlatformCacheConfiguration implements CachingConfigurer { + + @Autowired + private RuntimeDelegatingCacheManager delegatingCacheManager; + + @Bean + @Override + public CacheManager cacheManager() { + return this.delegatingCacheManager; + } + + @Override + public KeyGenerator keyGenerator() { + return new DefaultKeyGenerator(); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/cache/RuntimeDelegatingCacheManager.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/cache/RuntimeDelegatingCacheManager.java new file mode 100644 index 000000000..7b26b555e --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/cache/RuntimeDelegatingCacheManager.java @@ -0,0 +1,50 @@ +package org.mifosplatform.infrastructure.cache; + +import java.util.Collection; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.cache.ehcache.EhCacheCacheManager; +import org.springframework.cache.support.NoOpCacheManager; +import org.springframework.stereotype.Component; + +/** + * At present this implementation of {@link CacheManager} just delegates to the + * real {@link CacheManager} to use. + * + * By default it is {@link NoOpCacheManager} but we can change that by checking + * some persisted configuration in the database on startup and allow user to + * switch implementation through UI/API + */ +@Component +public class RuntimeDelegatingCacheManager implements CacheManager { + + private final CacheManager ehcacheCacheManager; + private final CacheManager noOpCacheManager = new NoOpCacheManager(); + private CacheManager currentCacheManager = this.noOpCacheManager; + + @Autowired + public RuntimeDelegatingCacheManager(final EhCacheCacheManager ehCacheCacheManager) { + this.ehcacheCacheManager = ehCacheCacheManager; + } + + @Override + public Cache getCache(final String name) { + return this.currentCacheManager.getCache(name); + } + + @Override + public Collection getCacheNames() { + return this.currentCacheManager.getCacheNames(); + } + + // TODO - Annuruddha - you need to create API to call to switch caching + // approach from NoOp to Single instance Echache - we can tackle the + // distributed cache through memcache then - didnt see the configuration for + // that. + // example method to switch caching mechanism used + private void switchToEhCache() { + this.currentCacheManager = this.ehcacheCacheManager; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/domain/MifosPlatformTenant.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/domain/MifosPlatformTenant.java index 150f073df..46d56749d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/domain/MifosPlatformTenant.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/domain/MifosPlatformTenant.java @@ -5,53 +5,23 @@ */ package org.mifosplatform.infrastructure.core.domain; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Table; +public class MifosPlatformTenant { -import org.springframework.data.jpa.domain.AbstractPersistable; + 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; - -@Entity -@Table(name = "tenants") -public class MifosPlatformTenant extends AbstractPersistable{ - - @Column(name="identifier") - private String tenantIdentifier; - - @Column(name = "name") - private String name; - - @Column(name = "schema_name") - private String schemaName; - - @Column(name = "schema_server") - private String schemaServer; - - @Column(name = "schema_server_port") - private String schemaServerPort; - - @Column(name = "schema_username") - private String schemaUsername; - - @Column(name = "schema_password") - private String schemaPassword; - - @Column(name = "timezone_id") - private String timezoneId; - - @Column(name = "auto_update") - private boolean autoUpdateEnabled; - - - public MifosPlatformTenant() { - - } - - 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, String timezoneId, - final boolean autoUpdateEnabled) { - setId(id); + 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) { + this.id = id; this.tenantIdentifier = tenantIdentifier; this.name = name; this.schemaName = schemaName; @@ -61,22 +31,24 @@ public class MifosPlatformTenant extends AbstractPersistable{ this.schemaPassword = schemaPassword; this.timezoneId = timezoneId; this.autoUpdateEnabled = autoUpdateEnabled; - } public String databaseURL() { - String url = new StringBuilder("jdbc:mysql://").append(schemaServer).append(':').append(schemaServerPort).append('/') - .append(schemaName).toString(); + final String url = new StringBuilder("jdbc:mysql://").append(this.schemaServer).append(':').append(this.schemaServerPort) + .append('/').append(this.schemaName).toString(); return url; } - + public Long getId() { + return this.id; + } + public String getTenantIdentifier() { return this.tenantIdentifier; } public String getName() { - return name; + return this.name; } public String getSchemaName() { @@ -84,15 +56,15 @@ public class MifosPlatformTenant extends AbstractPersistable{ } public String getSchemaUsername() { - return schemaUsername; + return this.schemaUsername; } public String getSchemaPassword() { - return schemaPassword; + return this.schemaPassword; } public String getTimezoneId() { - return timezoneId; + return this.timezoneId; } public boolean isAutoUpdateEnabled() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/filter/TenantAwareBasicAuthenticationFilter.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/filter/TenantAwareBasicAuthenticationFilter.java index 958ecb7c2..688ec6d61 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/filter/TenantAwareBasicAuthenticationFilter.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/filter/TenantAwareBasicAuthenticationFilter.java @@ -20,7 +20,7 @@ import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer; import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil; import org.mifosplatform.infrastructure.security.data.PlatformRequestLog; import org.mifosplatform.infrastructure.security.exception.InvalidTenantIdentiferException; -import org.mifosplatform.infrastructure.security.service.TenantDetailsService; +import org.mifosplatform.infrastructure.security.service.BasicAuthTenantDetailsService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -28,47 +28,51 @@ import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; +import org.springframework.stereotype.Service; /** * A customised version of spring security's {@link BasicAuthenticationFilter}. - * + * * This filter is responsible for extracting multi-tenant and basic auth * credentials from the request and checking that the details provided are * valid. - * + * * If multi-tenant and basic auth credentials are valid, the details of the * tenant are stored in {@link MifosPlatformTenant} and stored in a * {@link ThreadLocal} variable for this request using * {@link ThreadLocalContextUtil}. - * + * * If multi-tenant and basic auth credentials are invalid, a http error response * is returned. */ +@Service(value = "basicAuthenticationProcessingFilter") public class TenantAwareBasicAuthenticationFilter extends BasicAuthenticationFilter { private final static Logger logger = LoggerFactory.getLogger(TenantAwareBasicAuthenticationFilter.class); - @Autowired - private TenantDetailsService tenantDetailsService; + private final BasicAuthTenantDetailsService basicAuthTenantDetailsService; + private final ToApiJsonSerializer toApiJsonSerializer; + + private final String tenantRequestHeader = "X-Mifos-Platform-TenantId"; + private final boolean exceptionIfHeaderMissing = true; @Autowired - private ToApiJsonSerializer toApiJsonSerializer; - - private String tenantRequestHeader = "X-Mifos-Platform-TenantId"; - private boolean exceptionIfHeaderMissing = true; - public TenantAwareBasicAuthenticationFilter(final AuthenticationManager authenticationManager, - final AuthenticationEntryPoint authenticationEntryPoint) { + final AuthenticationEntryPoint authenticationEntryPoint, + final BasicAuthTenantDetailsService basicAuthTenantDetailsService, + final ToApiJsonSerializer toApiJsonSerializer) { super(authenticationManager, authenticationEntryPoint); + this.basicAuthTenantDetailsService = basicAuthTenantDetailsService; + this.toApiJsonSerializer = toApiJsonSerializer; } @Override - public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { + public void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain) throws IOException, ServletException { - HttpServletRequest request = (HttpServletRequest) req; - HttpServletResponse response = (HttpServletResponse) res; + final HttpServletRequest request = (HttpServletRequest) req; + final HttpServletResponse response = (HttpServletResponse) res; - StopWatch task = new StopWatch(); + final StopWatch task = new StopWatch(); task.start(); try { @@ -78,23 +82,23 @@ public class TenantAwareBasicAuthenticationFilter extends BasicAuthenticationFil // in different origin (domain name) } else { - String tenantId = request.getHeader(tenantRequestHeader); - if (org.apache.commons.lang.StringUtils.isBlank(tenantId)) { - tenantId = request.getParameter("tenantIdentifier"); + String tenantIdentifier = request.getHeader(this.tenantRequestHeader); + if (org.apache.commons.lang.StringUtils.isBlank(tenantIdentifier)) { + tenantIdentifier = request.getParameter("tenantIdentifier"); } - if (tenantId == null && exceptionIfHeaderMissing) { throw new InvalidTenantIdentiferException( - "No tenant identifier found: Add request header of '" + tenantRequestHeader + if (tenantIdentifier == null && this.exceptionIfHeaderMissing) { throw new InvalidTenantIdentiferException( + "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.tenantDetailsService.loadTenantById(tenantId); + final MifosPlatformTenant tenant = this.basicAuthTenantDetailsService.loadTenantById(tenantIdentifier); ThreadLocalContextUtil.setTenant(tenant); } super.doFilter(req, res, chain); - } catch (InvalidTenantIdentiferException e) { + } catch (final InvalidTenantIdentiferException e) { // deal with exception at low level SecurityContextHolder.getContext().setAuthentication(null); @@ -103,7 +107,7 @@ public class TenantAwareBasicAuthenticationFilter extends BasicAuthenticationFil } finally { task.stop(); final PlatformRequestLog log = PlatformRequestLog.from(task, request); - logger.info(toApiJsonSerializer.serialize(log)); + logger.info(this.toApiJsonSerializer.serialize(log)); } } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/service/BasicAuthTenantDetailsService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/service/BasicAuthTenantDetailsService.java new file mode 100644 index 000000000..065a0a539 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/service/BasicAuthTenantDetailsService.java @@ -0,0 +1,13 @@ +/** + * 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.security.service; + +import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenant; + +public interface BasicAuthTenantDetailsService { + + MifosPlatformTenant loadTenantById(String tenantId); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/service/BasicAuthTenantDetailsServiceJdbc.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/service/BasicAuthTenantDetailsServiceJdbc.java new file mode 100644 index 000000000..7515686af --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/service/BasicAuthTenantDetailsServiceJdbc.java @@ -0,0 +1,80 @@ +/** + * 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.security.service; + +import java.sql.ResultSet; +import java.sql.SQLException; + +import javax.sql.DataSource; + +import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenant; +import org.mifosplatform.infrastructure.security.exception.InvalidTenantIdentiferException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +/** + * A JDBC implementation of {@link BasicAuthTenantDetailsService} for loading a + * tenants details by a tenantIdentifier. + */ +@Service +public class BasicAuthTenantDetailsServiceJdbc implements BasicAuthTenantDetailsService { + + private final JdbcTemplate jdbcTemplate; + + @Autowired + public BasicAuthTenantDetailsServiceJdbc(@Qualifier("tenantDataSourceJndi") final DataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + private static final class TenantMapper implements RowMapper { + + 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 ")// + .append(" from tenants t");// + + public String schema() { + 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 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"); + + return new MifosPlatformTenant(id, tenantIdentifier, name, schemaName, schemaServer, schemaServerPort, schemaUsername, + schemaPassword, timezoneId, autoUpdateEnabled); + } + } + + @Override + @Cacheable(value = "tenantsById") + public MifosPlatformTenant loadTenantById(final String tenantIdentifier) { + + try { + final TenantMapper rm = new TenantMapper(); + final String sql = "select " + rm.schema() + " where t.identifier like ?"; + + return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { tenantIdentifier }); + } catch (final EmptyResultDataAccessException e) { + throw new InvalidTenantIdentiferException("The tenant identifier: " + tenantIdentifier + " is not valid."); + } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/service/JdbcTenantDetailsService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/service/JdbcTenantDetailsService.java index f7d947b2b..4282fa597 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/service/JdbcTenantDetailsService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/service/JdbcTenantDetailsService.java @@ -37,28 +37,28 @@ public class JdbcTenantDetailsService implements TenantDetailsService { private static final class TenantMapper implements RowMapper { - private StringBuilder sqlBuilder = new StringBuilder( + 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 ")// .append(" from tenants t");// public String schema() { - return sqlBuilder.toString(); + return this.sqlBuilder.toString(); } @Override public MifosPlatformTenant mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { - Long id = rs.getLong("id"); - String tenantIdentifier = rs.getString("identifier"); - String name = rs.getString("name"); - String schemaName = rs.getString("schemaName"); - String schemaServer = rs.getString("schemaServer"); - String schemaServerPort = rs.getString("schemaServerPort"); - String schemaUsername = rs.getString("schemaUsername"); - String schemaPassword = rs.getString("schemaPassword"); - String timezoneId = rs.getString("timezoneId"); - boolean autoUpdateEnabled = rs.getBoolean("autoUpdate"); + final Long id = rs.getLong("id"); + final String tenantIdentifier = rs.getString("identifier"); + final String name = rs.getString("name"); + 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"); return new MifosPlatformTenant(id, tenantIdentifier,name, schemaName, schemaServer, schemaServerPort, schemaUsername, schemaPassword, timezoneId, autoUpdateEnabled); @@ -66,25 +66,25 @@ public class JdbcTenantDetailsService implements TenantDetailsService { } @Override - @Cacheable(value = "tenantsById", key = "#tenantIdentifier") + @Cacheable(value = "tenantsById") public MifosPlatformTenant loadTenantById(final String tenantIdentifier) { try { - TenantMapper rm = new TenantMapper(); - String sql = "select " + rm.schema() + " where t.identifier like ?"; + final TenantMapper rm = new TenantMapper(); + final String sql = "select " + rm.schema() + " where t.identifier like ?"; return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { tenantIdentifier }); - } catch (EmptyResultDataAccessException e) { + } catch (final EmptyResultDataAccessException e) { throw new InvalidTenantIdentiferException("The tenant identifier: " + tenantIdentifier + " is not valid."); } } @Override public List findAllTenants() { - TenantMapper rm = new TenantMapper(); - String sql = "select " + rm.schema(); + final TenantMapper rm = new TenantMapper(); + final String sql = "select " + rm.schema(); - List mifosPlatformTenants = jdbcTemplate.query(sql, rm, new Object[] {}); + final List mifosPlatformTenants = this.jdbcTemplate.query(sql, rm, new Object[] {}); return mifosPlatformTenants; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/service/TenantDetailsService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/service/TenantDetailsService.java index cb58b8870..b979665cb 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/service/TenantDetailsService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/security/service/TenantDetailsService.java @@ -14,5 +14,4 @@ public interface TenantDetailsService { MifosPlatformTenant loadTenantById(String tenantId); List findAllTenants(); - -} +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/api/OfficesApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/api/OfficesApiResource.java index 6e9c6c249..41ec1a823 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/api/OfficesApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/api/OfficesApiResource.java @@ -53,7 +53,7 @@ public class OfficesApiResource { private final OfficeReadPlatformService readPlatformService; private final DefaultToApiJsonSerializer toApiJsonSerializer; private final ApiRequestParameterHelper apiRequestParameterHelper; - private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; @Autowired public OfficesApiResource(final PlatformSecurityContext context, final OfficeReadPlatformService readPlatformService, @@ -71,12 +71,12 @@ public class OfficesApiResource { @Produces({ MediaType.APPLICATION_JSON }) public String retrieveOffices(@Context final UriInfo uriInfo) { - context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions); + this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions); final Collection offices = this.readPlatformService.retrieveAllOffices(); - final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); - return this.toApiJsonSerializer.serialize(settings, offices, RESPONSE_DATA_PARAMETERS); + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, offices, this.RESPONSE_DATA_PARAMETERS); } @GET @@ -85,15 +85,15 @@ public class OfficesApiResource { @Produces({ MediaType.APPLICATION_JSON }) public String retrieveOfficeTemplate(@Context final UriInfo uriInfo) { - context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions); + this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions); OfficeData office = this.readPlatformService.retrieveNewOfficeTemplate(); final Collection allowedParents = this.readPlatformService.retrieveAllOfficesForDropdown(); office = OfficeData.appendedTemplate(office, allowedParents); - final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); - return this.toApiJsonSerializer.serialize(settings, office, RESPONSE_DATA_PARAMETERS); + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, office, this.RESPONSE_DATA_PARAMETERS); } @POST @@ -117,17 +117,17 @@ public class OfficesApiResource { @Produces({ MediaType.APPLICATION_JSON }) public String retreiveOffice(@PathParam("officeId") final Long officeId, @Context final UriInfo uriInfo) { - context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions); + this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions); - final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); OfficeData office = this.readPlatformService.retrieveOffice(officeId); if (settings.isTemplate()) { - Collection allowedParents = this.readPlatformService.retrieveAllowedParents(officeId); + final Collection allowedParents = this.readPlatformService.retrieveAllowedParents(officeId); office = OfficeData.appendedTemplate(office, allowedParents); } - return this.toApiJsonSerializer.serialize(settings, office, RESPONSE_DATA_PARAMETERS); + return this.toApiJsonSerializer.serialize(settings, office, this.RESPONSE_DATA_PARAMETERS); } @PUT diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeReadPlatformService.java index faa891020..b01582dc0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeReadPlatformService.java @@ -7,11 +7,14 @@ package org.mifosplatform.organisation.office.service; import java.util.Collection; +import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenant; import org.mifosplatform.organisation.office.data.OfficeData; import org.mifosplatform.organisation.office.data.OfficeTransactionData; public interface OfficeReadPlatformService { + MifosPlatformTenant loadTenantById(String tenantIdentifier); + Collection retrieveAllOffices(); Collection retrieveAllOfficesForDropdown(); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeReadPlatformServiceImpl.java index 8067690c9..31aa156cc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeReadPlatformServiceImpl.java @@ -11,9 +11,13 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; +import javax.sql.DataSource; + import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.domain.JdbcSupport; +import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenant; import org.mifosplatform.infrastructure.core.service.RoutingDataSource; +import org.mifosplatform.infrastructure.security.exception.InvalidTenantIdentiferException; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.organisation.monetary.service.CurrencyReadPlatformService; @@ -22,6 +26,7 @@ import org.mifosplatform.organisation.office.data.OfficeTransactionData; import org.mifosplatform.organisation.office.exception.OfficeNotFoundException; import org.mifosplatform.useradministration.domain.AppUser; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.cache.annotation.Cacheable; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; @@ -32,16 +37,19 @@ import org.springframework.stereotype.Service; public class OfficeReadPlatformServiceImpl implements OfficeReadPlatformService { private final JdbcTemplate jdbcTemplate; + private final JdbcTemplate tenantsJdbcTemplate; private final PlatformSecurityContext context; private final CurrencyReadPlatformService currencyReadPlatformService; private final static String nameDecoratedBaseOnHierarchy = "concat(substring('........................................', 1, ((LENGTH(o.hierarchy) - LENGTH(REPLACE(o.hierarchy, '.', '')) - 1) * 4)), o.name)"; @Autowired public OfficeReadPlatformServiceImpl(final PlatformSecurityContext context, - final CurrencyReadPlatformService currencyReadPlatformService, final RoutingDataSource dataSource) { + final CurrencyReadPlatformService currencyReadPlatformService, final RoutingDataSource dataSource, + @Qualifier("tenantDataSourceJndi") final DataSource tenantsJndiDataSource) { this.context = context; this.currencyReadPlatformService = currencyReadPlatformService; this.jdbcTemplate = new JdbcTemplate(dataSource); + this.tenantsJdbcTemplate = new JdbcTemplate(tenantsJndiDataSource); } private static final class OfficeMapper implements RowMapper { @@ -101,25 +109,25 @@ public class OfficeReadPlatformServiceImpl implements OfficeReadPlatformService @Override public OfficeTransactionData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { - Long id = rs.getLong("id"); - LocalDate transactionDate = JdbcSupport.getLocalDate(rs, "transactionDate"); - Long fromOfficeId = JdbcSupport.getLong(rs, "fromOfficeId"); - String fromOfficeName = rs.getString("fromOfficeName"); - Long toOfficeId = JdbcSupport.getLong(rs, "toOfficeId"); - String toOfficeName = rs.getString("toOfficeName"); + final Long id = rs.getLong("id"); + final LocalDate transactionDate = JdbcSupport.getLocalDate(rs, "transactionDate"); + final Long fromOfficeId = JdbcSupport.getLong(rs, "fromOfficeId"); + final String fromOfficeName = rs.getString("fromOfficeName"); + final Long toOfficeId = JdbcSupport.getLong(rs, "toOfficeId"); + final String toOfficeName = rs.getString("toOfficeName"); - String currencyCode = rs.getString("currencyCode"); - String currencyName = rs.getString("currencyName"); - String currencyNameCode = rs.getString("currencyNameCode"); - String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); - Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); - CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, - currencyDisplaySymbol, currencyNameCode); + final CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol, + currencyNameCode); - BigDecimal transactionAmount = rs.getBigDecimal("transactionAmount"); - String description = rs.getString("description"); + final BigDecimal transactionAmount = rs.getBigDecimal("transactionAmount"); + final String description = rs.getString("description"); return OfficeTransactionData.instance(id, transactionDate, fromOfficeId, fromOfficeName, toOfficeId, toOfficeName, currencyData, transactionAmount, description); @@ -130,13 +138,13 @@ public class OfficeReadPlatformServiceImpl implements OfficeReadPlatformService @Cacheable(value = "offices", key = "T(org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat(#root.target.context.authenticatedUser().getOffice().getHierarchy()+'of')") public Collection retrieveAllOffices() { - AppUser currentUser = context.authenticatedUser(); + final AppUser currentUser = this.context.authenticatedUser(); - String hierarchy = currentUser.getOffice().getHierarchy(); - String hierarchySearchString = hierarchy + "%"; + final String hierarchy = currentUser.getOffice().getHierarchy(); + final String hierarchySearchString = hierarchy + "%"; - OfficeMapper rm = new OfficeMapper(); - String sql = "select " + rm.officeSchema() + "where o.hierarchy like ? order by o.hierarchy"; + final OfficeMapper rm = new OfficeMapper(); + final String sql = "select " + rm.officeSchema() + "where o.hierarchy like ? order by o.hierarchy"; return this.jdbcTemplate.query(sql, rm, new Object[] { hierarchySearchString }); } @@ -144,7 +152,7 @@ public class OfficeReadPlatformServiceImpl implements OfficeReadPlatformService @Override @Cacheable(value = "officesForDropdown", key = "T(org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat(#root.target.context.authenticatedUser().getOffice().getHierarchy()+'ofd')") public Collection retrieveAllOfficesForDropdown() { - final AppUser currentUser = context.authenticatedUser(); + final AppUser currentUser = this.context.authenticatedUser(); final String hierarchy = currentUser.getOffice().getHierarchy(); final String hierarchySearchString = hierarchy + "%"; @@ -160,15 +168,15 @@ public class OfficeReadPlatformServiceImpl implements OfficeReadPlatformService public OfficeData retrieveOffice(final Long officeId) { try { - context.authenticatedUser(); + this.context.authenticatedUser(); - OfficeMapper rm = new OfficeMapper(); - String sql = "select " + rm.officeSchema() + " where o.id = ?"; + final OfficeMapper rm = new OfficeMapper(); + final String sql = "select " + rm.officeSchema() + " where o.id = ?"; - OfficeData selectedOffice = this.jdbcTemplate.queryForObject(sql, rm, new Object[] { officeId }); + final OfficeData selectedOffice = this.jdbcTemplate.queryForObject(sql, rm, new Object[] { officeId }); return selectedOffice; - } catch (EmptyResultDataAccessException e) { + } catch (final EmptyResultDataAccessException e) { throw new OfficeNotFoundException(officeId); } } @@ -176,7 +184,7 @@ public class OfficeReadPlatformServiceImpl implements OfficeReadPlatformService @Override public OfficeData retrieveNewOfficeTemplate() { - context.authenticatedUser(); + this.context.authenticatedUser(); return OfficeData.template(null, new LocalDate()); } @@ -184,13 +192,13 @@ public class OfficeReadPlatformServiceImpl implements OfficeReadPlatformService @Override public Collection retrieveAllowedParents(final Long officeId) { - context.authenticatedUser(); - Collection filterParentLookups = new ArrayList(); + this.context.authenticatedUser(); + final Collection filterParentLookups = new ArrayList(); if (isNotHeadOffice(officeId)) { - Collection parentLookups = retrieveAllOfficesForDropdown(); + final Collection parentLookups = retrieveAllOfficesForDropdown(); - for (OfficeData office : parentLookups) { + for (final OfficeData office : parentLookups) { if (!office.hasIdentifyOf(officeId)) { filterParentLookups.add(office); } @@ -207,13 +215,13 @@ public class OfficeReadPlatformServiceImpl implements OfficeReadPlatformService @Override public Collection retrieveAllOfficeTransactions() { - AppUser currentUser = context.authenticatedUser(); + final AppUser currentUser = this.context.authenticatedUser(); - String hierarchy = currentUser.getOffice().getHierarchy(); - String hierarchySearchString = hierarchy + "%"; + final String hierarchy = currentUser.getOffice().getHierarchy(); + final String hierarchySearchString = hierarchy + "%"; - OfficeTransactionMapper rm = new OfficeTransactionMapper(); - String sql = "select " + rm.schema() + final OfficeTransactionMapper rm = new OfficeTransactionMapper(); + final String sql = "select " + rm.schema() + " where (fromoff.hierarchy like ? or tooff.hierarchy like ?) order by ot.transaction_date, ot.id"; return this.jdbcTemplate.query(sql, rm, new Object[] { hierarchySearchString, hierarchySearchString }); @@ -221,15 +229,59 @@ public class OfficeReadPlatformServiceImpl implements OfficeReadPlatformService @Override public OfficeTransactionData retrieveNewOfficeTransactionDetails() { - context.authenticatedUser(); + this.context.authenticatedUser(); final Collection parentLookups = retrieveAllOfficesForDropdown(); - final Collection currencyOptions = currencyReadPlatformService.retrieveAllowedCurrencies(); + final Collection currencyOptions = this.currencyReadPlatformService.retrieveAllowedCurrencies(); return OfficeTransactionData.template(new LocalDate(), parentLookups, currencyOptions); } - + public PlatformSecurityContext getContext() { return this.context; } + + @Override + @Cacheable(value = "tenantsById", key="#tenantIdentifier") + public MifosPlatformTenant loadTenantById(final String tenantIdentifier) { + + try { + final TenantMapper rm = new TenantMapper(); + final String sql = "select " + rm.schema() + " where t.identifier like ?"; + + return this.tenantsJdbcTemplate.queryForObject(sql, rm, new Object[] { tenantIdentifier }); + } catch (final EmptyResultDataAccessException e) { + throw new InvalidTenantIdentiferException("The tenant identifier: " + tenantIdentifier + " is not valid."); + } + } + + private static final class TenantMapper implements RowMapper { + + 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 ")// + .append(" from tenants t");// + + public String schema() { + 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 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"); + + return new MifosPlatformTenant(id, tenantIdentifier, name, schemaName, schemaServer, schemaServerPort, schemaUsername, + schemaPassword, timezoneId, autoUpdateEnabled); + } + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/Role.java b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/Role.java index 34a45a818..63cadab54 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/Role.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/Role.java @@ -5,7 +5,6 @@ */ package org.mifosplatform.useradministration.domain; -import java.io.Serializable; import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashMap; @@ -26,7 +25,7 @@ import org.mifosplatform.useradministration.data.RoleData; import org.springframework.data.jpa.domain.AbstractPersistable; @Entity -@Table(name = "m_role", uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }, name = "unq_name")}) +@Table(name = "m_role", uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }, name = "unq_name") }) public class Role extends AbstractPersistable { @Column(name = "name", unique = true, nullable = false, length = 100) @@ -100,7 +99,7 @@ public class Role extends AbstractPersistable { public boolean hasPermissionTo(final String permissionCode) { boolean match = false; - for (Permission permission : this.permissions) { + for (final Permission permission : this.permissions) { if (permission.hasCode(permissionCode)) { match = true; break; @@ -110,6 +109,6 @@ public class Role extends AbstractPersistable { } public RoleData toData() { - return new RoleData(this.getId(), this.name, this.description); + return new RoleData(getId(), this.name, this.description); } } \ No newline at end of file diff --git a/mifosng-provider/src/main/resources/META-INF/spring/appContext.xml b/mifosng-provider/src/main/resources/META-INF/spring/appContext.xml index 7d47e30fa..aecaf7a86 100644 --- a/mifosng-provider/src/main/resources/META-INF/spring/appContext.xml +++ b/mifosng-provider/src/main/resources/META-INF/spring/appContext.xml @@ -24,8 +24,6 @@ - - - + - diff --git a/mifosng-provider/src/main/resources/META-INF/spring/securityContext.xml b/mifosng-provider/src/main/resources/META-INF/spring/securityContext.xml index 0cb43ea5d..f4731f9af 100644 --- a/mifosng-provider/src/main/resources/META-INF/spring/securityContext.xml +++ b/mifosng-provider/src/main/resources/META-INF/spring/securityContext.xml @@ -29,11 +29,12 @@ +