blob: 0bb0dee089cff42c45894a85b73b35d36f101944 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.mifos.provisioner.internal.service;
import io.mifos.provisioner.api.v1.domain.CassandraConnectionInfo;
import io.mifos.provisioner.api.v1.domain.DatabaseConnectionInfo;
import io.mifos.provisioner.api.v1.domain.Tenant;
import io.mifos.provisioner.config.ProvisionerConstants;
import io.mifos.provisioner.config.ProvisionerProperties;
import io.mifos.provisioner.internal.repository.TenantCassandraRepository;
import io.mifos.provisioner.internal.repository.TenantDAO;
import io.mifos.provisioner.internal.repository.TenantEntity;
import io.mifos.provisioner.internal.service.applications.IdentityServiceInitializer;
import io.mifos.provisioner.internal.util.DataSourceUtils;
import io.mifos.provisioner.internal.util.DataStoreOption;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nonnull;
import org.apache.fineract.cn.anubis.api.v1.domain.ApplicationSignatureSet;
import org.apache.fineract.cn.anubis.repository.TenantAuthorizationDataRepository;
import org.apache.fineract.cn.lang.AutoTenantContext;
import org.apache.fineract.cn.lang.ServiceException;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
@SuppressWarnings({"SqlNoDataSourceInspection", "SqlDialectInspection"})
@Component
public class TenantService {
private static final String META_KEYSPACE = "seshat"; //TODO: read MariaDB name from the configuration.
private final Logger logger;
private final Environment environment;
private final TenantApplicationService tenantApplicationService;
private final TenantAuthorizationDataRepository tenantAuthorizationDataRepository;
private final TenantCassandraRepository tenantCassandraRepository;
private final IdentityServiceInitializer identityServiceInitializer;
private final ProvisionerProperties provisionerProperties;
@Autowired
public TenantService(@Qualifier(ProvisionerConstants.LOGGER_NAME) final Logger logger,
final Environment environment,
final TenantApplicationService tenantApplicationService,
@SuppressWarnings("SpringJavaAutowiringInspection") final TenantAuthorizationDataRepository tenantAuthorizationDataRepository,
final TenantCassandraRepository tenantCassandraRepository,
final IdentityServiceInitializer identityServiceInitializer,
final ProvisionerProperties provisionerProperties) {
super();
this.logger = logger;
this.environment = environment;
this.tenantApplicationService = tenantApplicationService;
this.tenantAuthorizationDataRepository = tenantAuthorizationDataRepository;
this.tenantCassandraRepository = tenantCassandraRepository;
this.identityServiceInitializer = identityServiceInitializer;
this.provisionerProperties = provisionerProperties;
}
public void create(final Tenant tenant) {
this.initializeKeyspace(tenant);
this.initializeDatabase(tenant);
}
private void initializeKeyspace(final @Nonnull Tenant tenant) {
final DataStoreOption dataStoreOption = provisionerProperties.getDataStoreOption();
if (dataStoreOption.isEnabled(DataStoreOption.CASSANDRA)) {
final CassandraConnectionInfo cassandraConnectionInfo = tenant.getCassandraConnectionInfo();
final TenantEntity tenantEntity = new TenantEntity();
tenantEntity.setIdentifier(tenant.getIdentifier());
tenantEntity.setClusterName(cassandraConnectionInfo.getClusterName());
tenantEntity.setContactPoints(cassandraConnectionInfo.getContactPoints());
tenantEntity.setKeyspaceName(cassandraConnectionInfo.getKeyspace());
tenantEntity.setReplicationType(cassandraConnectionInfo.getReplicationType());
tenantEntity.setReplicas(cassandraConnectionInfo.getReplicas());
tenantEntity.setName(tenant.getName());
tenantEntity.setDescription(tenant.getDescription());
tenantEntity.setIdentityManagerApplicationName(null); //Identity manager can't be spun up till the io.mifos.provisioner.tenant is provisioned.
tenantEntity.setIdentityManagerApplicationUri(null); //Identity manager can't be spun up till the io.mifos.provisioner.tenant is provisioned.
tenantCassandraRepository.create(tenantEntity);
}
}
public Optional<String> assignIdentityManager(
final String tenantIdentifier,
final String identityManagerAppName,
final String identityManagerUri)
{
tenantCassandraRepository.adjust(tenantIdentifier, x -> {
x.setIdentityManagerApplicationName(identityManagerAppName);
x.setIdentityManagerApplicationUri(identityManagerUri);
});
IdentityServiceInitializer.IdentityServiceInitializationResult identityServiceInitializationResult = identityServiceInitializer.initializeIsis(tenantIdentifier, identityManagerAppName, identityManagerUri);
final ApplicationSignatureSet identityServiceTenantSignatureSet = identityServiceInitializationResult.getSignatureSet();
try (final AutoTenantContext ignored = new AutoTenantContext(tenantIdentifier)) {
tenantAuthorizationDataRepository.createSignatureSet(identityServiceTenantSignatureSet.getTimestamp(), identityServiceTenantSignatureSet.getIdentityManagerSignature());
}
return identityServiceInitializationResult.getAdminPassword();
}
public List<Tenant> fetchAll() {
final ArrayList<Tenant> result = new ArrayList<>();
this.fetchAllCassandra(result);
this.fetchAllDatabase(result);
return result;
}
private void fetchAllCassandra(final @Nonnull List<Tenant> tenants) {
final DataStoreOption dataStoreOption = provisionerProperties.getDataStoreOption();
if (dataStoreOption.isEnabled(DataStoreOption.CASSANDRA)) {
List<TenantEntity> tenantEntities = tenantCassandraRepository.fetchAll();
for (final TenantEntity tenantEntity : tenantEntities) {
final Tenant tenant = new Tenant();
tenants.add(tenant);
tenant.setIdentifier(tenantEntity.getIdentifier());
tenant.setName(tenantEntity.getName());
tenant.setDescription(tenantEntity.getDescription());
tenant.setCassandraConnectionInfo(getCassandraConnectionInfoFromTenantEntity(tenantEntity));
}
}
}
@SuppressWarnings("UnnecessaryLocalVariable")
public Optional<Tenant> find(final @Nonnull String identifier) {
final Optional<Tenant> tenantInCassandra = this.findCassandra(identifier);
final Optional<Tenant> tenantInDatabase = tenantInCassandra.map(x -> this.findInDatabase(x, identifier));
return tenantInDatabase;
}
public void delete(final String identifier) {
this.deleteFromCassandra(identifier);
this.deleteDatabase(identifier);
}
private void fetchAllDatabase(final ArrayList<Tenant> tenants) {
final DataStoreOption dataStoreOption = provisionerProperties.getDataStoreOption();
if (dataStoreOption.isEnabled(DataStoreOption.RDBMS)) {
if (tenants.size() > 0) {
try (final Connection connection = DataSourceUtils.createProvisionerConnection(this.environment)) {
for (final Tenant tenant : tenants) {
final Optional<TenantDAO> optionalTenantDAO = TenantDAO.find(connection, tenant.getIdentifier());
optionalTenantDAO.ifPresent(tenantDAO -> tenant.setDatabaseConnectionInfo(tenantDAO.map()));
}
} catch (final SQLException sqlex) {
this.logger.error(sqlex.getMessage(), sqlex);
throw new IllegalStateException("Could not load io.mifos.provisioner.tenant data!");
}
} else {
try (final Connection connection = DataSourceUtils.createProvisionerConnection(this.environment)) {
final List<TenantDAO> tenantDAOs = TenantDAO.fetchAll(connection);
for (final TenantDAO tenantDAO : tenantDAOs) {
final Tenant tenant = new Tenant();
tenants.add(tenant);
tenant.setIdentifier(tenantDAO.getIdentifier());
tenant.setDatabaseConnectionInfo(tenantDAO.map());
}
} catch (final SQLException sqlex) {
this.logger.error(sqlex.getMessage(), sqlex);
throw new IllegalStateException("Could not load io.mifos.provisioner.tenant data!");
}
}
}
}
private Optional<Tenant> findCassandra(final String identifier) {
final DataStoreOption dataStoreOption = provisionerProperties.getDataStoreOption();
if (dataStoreOption.isEnabled(DataStoreOption.CASSANDRA)) {
return tenantCassandraRepository.get(identifier).map(x -> {
final Tenant tenant = new Tenant();
tenant.setIdentifier(x.getIdentifier());
tenant.setName(x.getName());
tenant.setDescription(x.getDescription());
tenant.setCassandraConnectionInfo(getCassandraConnectionInfoFromTenantEntity(x));
return tenant;
}
);
}
return Optional.empty();
}
private Tenant findInDatabase(final @Nonnull Tenant tenant, final @Nonnull String identifier) {
final DataStoreOption dataStoreOption = provisionerProperties.getDataStoreOption();
if (dataStoreOption.isEnabled(DataStoreOption.RDBMS)) {
try (final Connection connection = DataSourceUtils.createProvisionerConnection(this.environment)) {
final Optional<TenantDAO> optionalTenantDAO = TenantDAO.find(connection, identifier);
if (optionalTenantDAO.isPresent()) {
tenant.setDatabaseConnectionInfo(optionalTenantDAO.get().map());
return tenant;
}
} catch (final SQLException sqlex) {
this.logger.error(sqlex.getMessage(), sqlex);
throw new IllegalStateException("Could not load io.mifos.provisioner.tenant data!");
}
}
return tenant;
}
private void initializeDatabase(final Tenant tenant) {
final DataStoreOption dataStoreOption = provisionerProperties.getDataStoreOption();
if (dataStoreOption.isEnabled(DataStoreOption.RDBMS)) {
try (
final Connection provisionerConnection = DataSourceUtils.createProvisionerConnection(this.environment);
final Statement statement = provisionerConnection.createStatement()
) {
final java.sql.ResultSet resultSet = statement.executeQuery(
" SELECT * FROM " + META_KEYSPACE + ".tenants WHERE identifier = '" + tenant.getIdentifier() + "' ");
if (resultSet.next()) {
throw ServiceException.conflict("Tenant {0} already exists!", tenant.getIdentifier());
}
} catch (SQLException sqlex) {
this.logger.error(sqlex.getMessage(), sqlex);
throw new IllegalStateException("Could not insert io.mifos.provisioner.tenant info!", sqlex);
}
final DatabaseConnectionInfo databaseConnectionInfo = tenant.getDatabaseConnectionInfo();
try (
final Connection connection = DataSourceUtils.create(databaseConnectionInfo);
final Statement statement = connection.createStatement()
) {
statement.execute("CREATE DATABASE IF NOT EXISTS " + databaseConnectionInfo.getDatabaseName());
statement.close();
} catch (final SQLException sqlex) {
this.logger.error(sqlex.getMessage(), sqlex);
throw new IllegalStateException("Could not create database!", sqlex);
}
try (final Connection provisionerConnection = DataSourceUtils.createProvisionerConnection(this.environment)) {
final TenantDAO tenantDAO = new TenantDAO();
tenantDAO.setIdentifier(tenant.getIdentifier());
tenantDAO.setDriverClass(databaseConnectionInfo.getDriverClass());
tenantDAO.setDatabaseName(databaseConnectionInfo.getDatabaseName());
tenantDAO.setHost(databaseConnectionInfo.getHost());
tenantDAO.setPort(databaseConnectionInfo.getPort());
tenantDAO.setUser(databaseConnectionInfo.getUser());
tenantDAO.setPassword(databaseConnectionInfo.getPassword());
tenantDAO.insert(provisionerConnection);
provisionerConnection.commit();
} catch (SQLException sqlex) {
this.logger.error(sqlex.getMessage(), sqlex);
throw new IllegalStateException("Could not insert io.mifos.provisioner.tenant info!", sqlex);
}
}
}
private void deleteFromCassandra(final @Nonnull String identifier) {
final DataStoreOption dataStoreOption = provisionerProperties.getDataStoreOption();
if (dataStoreOption.isEnabled(DataStoreOption.CASSANDRA)) {
final Optional<TenantEntity> tenantEntity = tenantCassandraRepository.get(identifier);
tenantEntity.ifPresent(x ->
{
tenantCassandraRepository.delete(identifier);
this.tenantApplicationService.deleteTenant(identifier);
});
}
}
private void deleteDatabase(final String identifier) {
final DataStoreOption dataStoreOption = provisionerProperties.getDataStoreOption();
if (dataStoreOption.isEnabled(DataStoreOption.RDBMS)) {
try (final Connection provisionerConnection = DataSourceUtils.createProvisionerConnection(this.environment)) {
final Optional<TenantDAO> optionalTenantDAO = TenantDAO.find(provisionerConnection, identifier);
if (optionalTenantDAO.isPresent()) {
final DatabaseConnectionInfo databaseConnectionInfo = optionalTenantDAO.get().map();
try (
final Connection connection = DataSourceUtils.create(databaseConnectionInfo);
final Statement dropStatement = connection.createStatement()
) {
dropStatement.execute("DROP DATABASE " + databaseConnectionInfo.getDatabaseName());
connection.commit();
}
TenantDAO.delete(provisionerConnection, identifier);
provisionerConnection.commit();
}
} catch (final SQLException sqlex) {
this.logger.error(sqlex.getMessage(), sqlex);
throw new IllegalStateException("Could not delete database!");
}
}
}
private static CassandraConnectionInfo getCassandraConnectionInfoFromTenantEntity(final TenantEntity tenantEntity) {
final CassandraConnectionInfo cassandraConnectionInfo = new CassandraConnectionInfo();
cassandraConnectionInfo.setClusterName(tenantEntity.getClusterName());
cassandraConnectionInfo.setContactPoints(tenantEntity.getContactPoints());
cassandraConnectionInfo.setKeyspace(tenantEntity.getKeyspaceName());
cassandraConnectionInfo.setReplicationType(tenantEntity.getReplicationType());
cassandraConnectionInfo.setReplicas(tenantEntity.getReplicas());
return cassandraConnectionInfo;
}
}