package org.molgenis.integrationtest.platform;
import com.google.common.io.Files;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.io.FileUtils;
import org.molgenis.DatabaseConfig;
import org.molgenis.data.EntityFactoryRegistrar;
import org.molgenis.data.RepositoryCollectionBootstrapper;
import org.molgenis.data.convert.DateToStringConverter;
import org.molgenis.data.convert.StringToDateConverter;
import org.molgenis.data.elasticsearch.config.EmbeddedElasticSearchConfig;
import org.molgenis.data.meta.system.SystemEntityTypeRegistrar;
import org.molgenis.data.meta.system.SystemPackageRegistrar;
import org.molgenis.data.platform.bootstrap.SystemEntityTypeBootstrapper;
import org.molgenis.data.platform.config.PlatformConfig;
import org.molgenis.data.populate.UuidGenerator;
import org.molgenis.data.postgresql.PostgreSqlConfiguration;
import org.molgenis.data.settings.AppSettings;
import org.molgenis.data.transaction.MolgenisTransactionManager;
import org.molgenis.data.validation.ExpressionValidator;
import org.molgenis.integrationtest.data.TestAppSettings;
import org.molgenis.security.core.MolgenisPasswordEncoder;
import org.molgenis.security.core.MolgenisPermissionService;
import org.molgenis.security.core.runas.RunAsSystemBeanPostProcessor;
import org.molgenis.security.core.runas.RunAsSystemProxy;
import org.molgenis.security.permission.MolgenisPermissionServiceImpl;
import org.molgenis.util.ApplicationContextProvider;
import org.molgenis.util.GsonConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.mail.MailSender;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.SocketUtils;
import javax.annotation.PreDestroy;
import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Properties;
import static org.mockito.Mockito.mock;
import static org.molgenis.data.postgresql.PostgreSqlRepositoryCollection.POSTGRESQL;
import static org.molgenis.integrationtest.platform.PostgreSqlDatabase.dropAndCreateDatabase;
@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
/*
FIXME Ideally, we'd like to scan all of org.molgenis.data or even org.molgenis, but there's some unwanted dependencies
in org.molgenis.data and subpackages from included modules
*/
@ComponentScan({ "org.molgenis.data.aggregation", "org.molgenis.data.meta", "org.molgenis.data.index",
"org.molgenis.data.jobs", "org.molgenis.js", "org.molgenis.data.elasticsearch", "org.molgenis.auth",
"org.molgenis.test.data", "org.molgenis.data.platform", "org.molgenis.data.meta.model",
"org.molgenis.data.system.model", "org.molgenis.data.cache", "org.molgenis.data.i18n",
"org.molgenis.data.postgresql", "org.molgenis.file.model", "org.molgenis.security.owned",
"org.molgenis.security.user", "org.molgenis.data.validation", "org.molgenis.data.transaction",
"org.molgenis.data.importer.emx", "org.molgenis.data.importer.config", "org.molgenis.data.excel" })
@Import({ DatabaseConfig.class, EmbeddedElasticSearchConfig.class, GsonConfig.class, PostgreSqlConfiguration.class,
RunAsSystemBeanPostProcessor.class, UuidGenerator.class, ExpressionValidator.class, PlatformConfig.class,
org.molgenis.data.RepositoryCollectionRegistry.class,
org.molgenis.data.RepositoryCollectionDecoratorFactory.class,
org.molgenis.data.RepositoryCollectionBootstrapper.class, org.molgenis.data.EntityFactoryRegistrar.class,
org.molgenis.data.importer.emx.EmxImportService.class, org.molgenis.data.importer.ImportServiceFactory.class,
org.molgenis.data.FileRepositoryCollectionFactory.class, org.molgenis.data.excel.ExcelDataConfig.class,
org.molgenis.security.permission.PermissionSystemService.class, org.molgenis.data.importer.ImportServiceRegistrar.class })
public class PlatformITConfig implements ApplicationListener<ContextRefreshedEvent>
{
private static final String INTEGRATION_TEST_DATABASE_NAME;
static
{
INTEGRATION_TEST_DATABASE_NAME = "molgenis_test_" + System.nanoTime();
dropAndCreateDatabase(INTEGRATION_TEST_DATABASE_NAME);
}
private final static Logger LOG = LoggerFactory.getLogger(PlatformITConfig.class);
@Autowired
private DataSource dataSource;
@Autowired
private RepositoryCollectionBootstrapper repoCollectionBootstrapper;
@Autowired
private SystemEntityTypeRegistrar systemEntityTypeRegistrar;
@Autowired
private SystemPackageRegistrar systemPackageRegistrar;
@Autowired
private EntityFactoryRegistrar entityFactoryRegistrar;
@Autowired
private SystemEntityTypeBootstrapper systemEntityTypeBootstrapper;
@Bean
public static PropertySourcesPlaceholderConfigurer properties()
{
String dbUriAdmin;
try
{
dbUriAdmin = PostgreSqlDatabase.getPostgreSqlDatabaseUri();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
Properties overwriteProperties = new Properties();
overwriteProperties.setProperty("db_uri",
dbUriAdmin + INTEGRATION_TEST_DATABASE_NAME + "?reWriteBatchedInserts=true&autosave=CONSERVATIVE");
overwriteProperties.setProperty("elasticsearch.transport.tcp.port",
String.valueOf(SocketUtils.findAvailableTcpPort(9301, 9400)));
PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();
Resource[] resources = new Resource[] { new ClassPathResource("/postgresql/molgenis.properties") };
pspc.setLocations(resources);
pspc.setFileEncoding("UTF-8");
pspc.setIgnoreUnresolvablePlaceholders(true);
pspc.setIgnoreResourceNotFound(true);
pspc.setNullValue("@null");
pspc.setProperties(overwriteProperties);
return pspc;
}
@Bean
public MailSender mailSender()
{
return mock(MailSender.class);
}
@PreDestroy
public void cleanup() throws IOException, SQLException
{
((ComboPooledDataSource) dataSource).close();
PostgreSqlDatabase.dropDatabase(INTEGRATION_TEST_DATABASE_NAME);
try
{
// Delete molgenis home folder
FileUtils.deleteDirectory(new File(System.getProperty("molgenis.home")));
}
catch (IOException e)
{
LOG.error("Error removing molgenis home directory", e);
}
}
public PlatformITConfig()
{
System.setProperty("molgenis.home", Files.createTempDir().getAbsolutePath());
}
@Bean
public AppSettings appSettings()
{
return new TestAppSettings();
}
@Bean
public ConversionService conversionService()
{
DefaultConversionService defaultConversionService = new DefaultConversionService();
defaultConversionService.addConverter(new DateToStringConverter());
defaultConversionService.addConverter(new StringToDateConverter());
return defaultConversionService;
}
@Bean
public ApplicationContextProvider applicationContextProvider()
{
return new ApplicationContextProvider();
}
@Bean
public PasswordEncoder passwordEncoder()
{
return new MolgenisPasswordEncoder(new BCryptPasswordEncoder());
}
@Bean
public MolgenisPermissionService molgenisPermissionService()
{
return new MolgenisPermissionServiceImpl();
}
// FIXME The bootstrapping of the data platform should be delegated to a specific bootstrapper so that updates
// are reflected in the test
@Autowired
MolgenisTransactionManager molgenisTransactionManager;
@Override
public void onApplicationEvent(ContextRefreshedEvent event)
{
TransactionTemplate transactionTemplate = new TransactionTemplate();
transactionTemplate.setTransactionManager(molgenisTransactionManager);
transactionTemplate.execute((action) ->
{
try
{
RunAsSystemProxy.runAsSystem(() ->
{
LOG.info("Bootstrapping registries ...");
LOG.trace("Registering repository collections ...");
repoCollectionBootstrapper.bootstrap(event, POSTGRESQL);
LOG.trace("Registered repository collections");
LOG.trace("Registering system entity meta data ...");
systemEntityTypeRegistrar.register(event);
LOG.trace("Registered system entity meta data");
LOG.trace("Registering system packages ...");
systemPackageRegistrar.register(event);
LOG.trace("Registered system packages");
LOG.trace("Registering entity factories ...");
entityFactoryRegistrar.register(event);
LOG.trace("Registered entity factories");
LOG.debug("Bootstrapped registries");
LOG.trace("Bootstrapping system entity types ...");
systemEntityTypeBootstrapper.bootstrap(event);
LOG.debug("Bootstrapped system entity types");
});
}
catch (Exception unexpected)
{
LOG.error("Error bootstrapping tests!", unexpected);
throw new RuntimeException(unexpected);
}
return (Void) null;
});
}
}