package com.constellio.sdk.tests;
import static com.constellio.data.dao.dto.records.RecordsFlushing.NOW;
import static com.constellio.sdk.tests.TestUtils.asList;
import static org.mockito.Mockito.spy;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.solr.common.params.ModifiableSolrParams;
import com.constellio.app.services.extensions.plugins.ConstellioPluginManager;
import com.constellio.app.services.factories.AppLayerFactory;
import com.constellio.app.services.factories.ConstellioFactories;
import com.constellio.app.ui.i18n.i18n;
import com.constellio.data.conf.ConfigManagerType;
import com.constellio.data.conf.ContentDaoType;
import com.constellio.data.conf.DataLayerConfiguration;
import com.constellio.data.dao.managers.StatefulService;
import com.constellio.data.dao.managers.StatefullServiceDecorator;
import com.constellio.data.dao.services.bigVault.solr.BigVaultException;
import com.constellio.data.dao.services.bigVault.solr.BigVaultException.CouldNotExecuteQuery;
import com.constellio.data.dao.services.bigVault.solr.BigVaultServer;
import com.constellio.data.dao.services.bigVault.solr.BigVaultServerTransaction;
import com.constellio.data.dao.services.factories.DataLayerFactory;
import com.constellio.data.extensions.DataLayerSystemExtensions;
import com.constellio.data.extensions.TransactionLogExtension;
import com.constellio.data.frameworks.extensions.ExtensionBooleanResult;
import com.constellio.data.io.IOServicesFactory;
import com.constellio.data.utils.Factory;
import com.constellio.model.conf.FoldersLocator;
import com.constellio.model.conf.PropertiesModelLayerConfiguration.InMemoryModelLayerConfiguration;
import com.constellio.model.entities.security.global.UserCredential;
import com.constellio.model.services.encrypt.EncryptionServices;
import com.constellio.model.services.factories.ModelLayerFactory;
import com.constellio.sdk.FakeEncryptionServices;
public class FactoriesTestFeatures {
private boolean fakeEncryptionServices;
private boolean useSDKPluginFolder;
//private boolean instanciated = false;
private boolean backgroundThreadsEnabled = false;
private boolean checkRollback;
private List<String> loggingOfRecords = new ArrayList<>();
private boolean dummyPasswords;
private File initialState;
private final FileSystemTestFeatures fileSystemTestFeatures;
//private ConstellioFactories factoriesInstance;
private List<Class<?>> spiedClasses = new ArrayList<>();
private Map<String, TestConstellioFactoriesDecorator> decorators = new HashMap<>();
// private Map<String, String> sdkProperties;
private List<DataLayerConfigurationAlteration> dataLayerConfigurationAlterations = new ArrayList<>();
private List<ModelLayerConfigurationAlteration> modelLayerConfigurationAlterations = new ArrayList<>();
private List<AppLayerConfigurationAlteration> appLayerConfigurationAlterations = new ArrayList<>();
private Map<String, String> configs = new HashMap<>();
private String systemLanguage;
public FactoriesTestFeatures(FileSystemTestFeatures fileSystemTestFeatures, Map<String, String> sdkProperties,
boolean checkRollback) {
this.fileSystemTestFeatures = fileSystemTestFeatures;
this.checkRollback = checkRollback;
// this.sdkProperties = sdkProperties;
ConstellioFactories.instanceProvider = new SDKConstellioFactoriesInstanceProvider();
}
public void afterTest() {
if (ConstellioFactories.instanceProvider.isInitialized()) {
clear();
}
ConstellioFactories.clear();
}
public void clear() {
SDKConstellioFactoriesInstanceProvider instanceProvider = (SDKConstellioFactoriesInstanceProvider) ConstellioFactories.instanceProvider;
for (ConstellioFactories factoriesInstance : instanceProvider.instances.values()) {
File licenseFile = factoriesInstance.getFoldersLocator().getLicenseFile();
if (licenseFile.exists()) {
licenseFile.delete();
}
DataLayerConfiguration conf = factoriesInstance.getDataLayerConfiguration();
for (BigVaultServer server : factoriesInstance.getDataLayerFactory().getSolrServers().getServers()) {
deleteServerRecords(server);
}
if (ContentDaoType.HADOOP == conf.getContentDaoType()) {
deleteFromHadoop(conf.getContentDaoHadoopUser(), conf.getContentDaoHadoopUrl());
}
if (ConfigManagerType.ZOOKEEPER == conf.getSettingsConfigType()) {
deleteFromZooKeeper(conf.getSettingsZookeeperAddress());
}
}
i18n.clearBundles();
}
private void deleteFromZooKeeper(String address) {
try {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
CuratorFramework client = CuratorFrameworkFactory.newClient(address, retryPolicy);
client.start();
client.delete().deletingChildrenIfNeeded().forPath("/constellio");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void deleteFromHadoop(String user, String url) {
System.setProperty("HADOOP_USER_NAME", user);
Configuration hadoopConfig = new Configuration();
if (url == null || user == null) {
throw new RuntimeException("No config");
}
hadoopConfig.set("fs.defaultFS", url);
hadoopConfig.set("hadoop.job.ugi", user);
try {
FileSystem hdfs = FileSystem.get(hadoopConfig);
for (FileStatus file : hdfs.globStatus(new Path("*"))) {
hdfs.delete(file.getPath(), true);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void deleteServerRecords(BigVaultServer server) {
BigVaultServer vaultServer = server.clone();
vaultServer.unregisterAllListeners();
vaultServer.disableLogger();
vaultServer.setExtensions(new DataLayerSystemExtensions());
ModifiableSolrParams allRecordsSolrParams = new ModifiableSolrParams();
allRecordsSolrParams.set("q", "*:*");
try {
vaultServer
.addAll(new BigVaultServerTransaction(NOW).addDeletedQuery("*:*"));
} catch (BigVaultException e) {
throw new RuntimeException("Cannot deleteLogically by query *:*");
}
try {
if (!vaultServer.query(allRecordsSolrParams).getResults().isEmpty()) {
throw new RuntimeException("Invalid solr core initial state ");
}
} catch (CouldNotExecuteQuery couldNotExecuteQuery) {
throw new RuntimeException(couldNotExecuteQuery);
}
}
public synchronized ConstellioFactories getConstellioFactories() {
return getConstellioFactories(SDKConstellioFactoriesInstanceProvider.DEFAULT_NAME);
}
public synchronized ConstellioFactories getConstellioFactories(final String name) {
TestConstellioFactoriesDecorator decorator = decorators.get(name);
if (decorator == null) {
StringBuilder setupPropertiesContent = new StringBuilder();
setupPropertiesContent.append("admin.servicekey=adminkey\n");
setupPropertiesContent.append("admin.password=password\n");
File setupProperties = fileSystemTestFeatures.newTempFileWithContent(setupPropertiesContent.toString());
decorator = new TestConstellioFactoriesDecorator(backgroundThreadsEnabled, true, checkRollback) {
@Override
public DataLayerFactory decorateDataLayerFactory(DataLayerFactory dataLayerFactory) {
if (!loggingOfRecords.isEmpty()) {
dataLayerFactory.getDataLayerLogger().setMonitoredIds(loggingOfRecords);
}
dataLayerFactory.getExtensions().getSystemWideExtensions().getTransactionLogExtensions()
.add(new TransactionLogExtension() {
@Override
public ExtensionBooleanResult isDocumentFieldLoggedInTransactionLog(String field, String schema,
String collection) {
return ExtensionBooleanResult.FORCE_TRUE;
}
});
if (spiedClasses.isEmpty()) {
return dataLayerFactory;
} else {
return spy(dataLayerFactory);
}
}
@Override
public ModelLayerFactory decorateModelServicesFactory(final ModelLayerFactory modelLayerFactory) {
if (spiedClasses.isEmpty()) {
return modelLayerFactory;
} else {
return spy(modelLayerFactory);
}
}
@Override
public AppLayerFactory decorateAppServicesFactory(final AppLayerFactory appLayerFactory) {
if (dummyPasswords) {
appLayerFactory.add(new StatefulService() {
@Override
public void initialize() {
try {
ModelLayerFactory modelLayerFactory = appLayerFactory.getModelLayerFactory();
List<UserCredential> users = modelLayerFactory.newUserServices().getAllUserCredentials();
StringBuilder passwordFileContent = new StringBuilder();
for (UserCredential user : users) {
passwordFileContent.append(user.getUsername() + "=W6ph5Mm5Pz8GgiULbPgzG37mj9g\\=\n");
}
File settingsFolder = modelLayerFactory.getDataLayerFactory().getDataLayerConfiguration()
.getSettingsFileSystemBaseFolder();
File authenticationFile = new File(settingsFolder, "authentification.properties");
try {
FileUtils.write(authenticationFile, passwordFileContent.toString());
} catch (IOException e) {
throw new RuntimeException(e);
}
} catch (Exception e) {
//e.printStackTrace();
}
}
@Override
public void close() {
}
});
}
if (spiedClasses.isEmpty()) {
return appLayerFactory;
} else {
return spy(appLayerFactory);
}
}
@Override
public StatefullServiceDecorator getStatefullServiceDecorator() {
return new StatefullServiceDecorator() {
@Override
public <T> T decorate(T service) {
if (service != null && ConstellioPluginManager.class.isAssignableFrom(service.getClass())) {
return spy(service);
} else {
for (Class<?> spiedClass : spiedClasses) {
if (spiedClass.isAssignableFrom(service.getClass())) {
return spy(service);
}
}
}
return service;
}
};
}
};
if (fakeEncryptionServices) {
modelLayerConfigurationAlterations.add(new ModelLayerConfigurationAlteration() {
@Override
public void alter(InMemoryModelLayerConfiguration configuration) {
Factory<EncryptionServices> encryptionServicesFactory = new Factory<EncryptionServices>() {
@Override
public EncryptionServices get() {
return new FakeEncryptionServices();
}
};
configuration.setEncryptionServicesFactory(encryptionServicesFactory);
}
});
}
File configManagerFolder = fileSystemTestFeatures.newTempFolderWithName("configManagerFolder");
File contentFolder = fileSystemTestFeatures.newTempFolderWithName("contentFolder");
File pluginsFolder;
if (useSDKPluginFolder) {
pluginsFolder = new SDKFoldersLocator().getPluginsJarsFolder();
} else {
pluginsFolder = fileSystemTestFeatures.newTempFolderWithName("plugins");
}
File tlogWorkFolder = fileSystemTestFeatures.newTempFolderWithName("tlogWorkFolder");
decorator.setDataLayerConfigurationAlterations(dataLayerConfigurationAlterations);
decorator.setModelLayerConfigurationAlterations(modelLayerConfigurationAlterations);
decorator.setAppLayerConfigurationAlterations(appLayerConfigurationAlterations);
decorator.setSetupProperties(setupProperties);
decorator.setImportationFolder(fileSystemTestFeatures.newTempFolderWithName("importationFolder"));
decorator.setConfigManagerFolder(configManagerFolder);
decorator.setAppTempFolder(fileSystemTestFeatures.newTempFolderWithName("appTempFolder"));
decorator.setContentFolder(contentFolder);
decorator.setPluginsFolder(pluginsFolder)
.setPluginsToMoveOnStartupFile(fileSystemTestFeatures.newTempFileWithContent(""));
decorator.setTransactionLogWorkFolder(tlogWorkFolder);
decorator.setSystemLanguage(systemLanguage);
if (initialState != null) {
if (!ConstellioTest.isCurrentPreservingState()) {
File tempFolder = fileSystemTestFeatures.newTempFolder();
try {
SaveStateFeature
.loadStateFrom(initialState, tempFolder, configManagerFolder, contentFolder, pluginsFolder,
tlogWorkFolder, dummyPasswords);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
decorators.put(name, decorator);
}
File propertyFile = new SDKFoldersLocator().getSDKProperties();
if (!configs.isEmpty()) {
File tempPropertyFile = fileSystemTestFeatures.newTempFileInNewTempFolder("temp-sdk.properties");
StringBuilder basePropertyContent = new StringBuilder();
try {
basePropertyContent.append(FileUtils.readFileToString(propertyFile) + "\n");
for (Map.Entry<String, String> entry : configs.entrySet()) {
basePropertyContent.append(entry.getKey() + "=" + entry.getValue() + "\n");
}
FileUtils.writeStringToFile(tempPropertyFile, basePropertyContent.toString());
} catch (IOException e) {
throw new RuntimeException(e);
}
propertyFile = tempPropertyFile;
}
final File finalPropertyFile = propertyFile;
final TestConstellioFactoriesDecorator finalDecorator = decorator;
SDKConstellioFactoriesInstanceProvider instanceProvider = (SDKConstellioFactoriesInstanceProvider) ConstellioFactories.instanceProvider;
return instanceProvider.getInstance(new Factory<ConstellioFactories>() {
@Override
public ConstellioFactories get() {
ConstellioFactories instance = ConstellioFactories.buildFor(finalPropertyFile, finalDecorator, name);
return instance;
}
}, name);
}
public void configure(DataLayerConfigurationAlteration dataLayerConfigurationAlteration) {
dataLayerConfigurationAlterations.add(dataLayerConfigurationAlteration);
}
public void configure(ModelLayerConfigurationAlteration modelLayerConfigurationAlteration) {
modelLayerConfigurationAlterations.add(modelLayerConfigurationAlteration);
}
public void configure(AppLayerConfigurationAlteration appLayerConfigurationAlteration) {
appLayerConfigurationAlterations.add(appLayerConfigurationAlteration);
}
public void load() {
getConstellioFactories();
}
public DataLayerFactory newDaosFactory(String name) {
return getConstellioFactories(name).getDataLayerFactory();
}
public IOServicesFactory newIOServicesFactory(String name) {
return getConstellioFactories(name).getIoServicesFactory();
}
public ModelLayerFactory newModelServicesFactory(String name) {
return getConstellioFactories(name).getModelLayerFactory();
}
public AppLayerFactory newAppServicesFactory(String name) {
return getConstellioFactories(name).getAppLayerFactory();
}
public FoldersLocator getFoldersLocator(String name) {
return getConstellioFactories(name).getFoldersLocator();
}
public void withSpiedServices(Class<?>[] classes) {
spiedClasses.addAll(asList(classes));
}
public void givenConstellioProperties(Map<String, String> configs) {
this.configs.putAll(configs);
}
public FactoriesTestFeatures givenSystemInState(File state) {
this.initialState = state;
return this;
}
public FactoriesTestFeatures withPasswordsReset() {
this.dummyPasswords = true;
return this;
}
public void setSystemLanguage(String languageCode) {
this.systemLanguage = languageCode;
}
public FactoriesTestFeatures withLoggingOfRecords(String... loggingOfRecordsArray) {
loggingOfRecords.addAll(asList(loggingOfRecordsArray));
return this;
}
public boolean isInitialized() {
return ConstellioFactories.instanceProvider.isInitialized();
}
public void givenBackgroundThreadsEnabled() {
backgroundThreadsEnabled = true;
}
public FactoriesTestFeatures withoutCheckForRollback() {
checkRollback = false;
return this;
}
public FactoriesTestFeatures withFakeEncryptionServices() {
fakeEncryptionServices = true;
return this;
}
public FactoriesTestFeatures withSDKPluginFolder() {
useSDKPluginFolder = true;
return this;
}
public boolean isCheckRollback() {
return checkRollback;
}
}