package com.constellio.app.services.recovery;
import static com.constellio.app.services.recovery.UpdateRecoveryImpossibleCause.TOO_SHORT_MEMORY;
import static com.constellio.app.services.recovery.UpdateRecoveryImpossibleCause.TOO_SHORT_SPACE;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.constellio.app.services.appManagement.AppManagementService;
import com.constellio.app.services.factories.AppLayerFactory;
import com.constellio.app.services.systemProperties.SystemPropertiesServices;
import com.constellio.data.conf.DataLayerConfiguration;
import com.constellio.data.dao.managers.config.ConfigManager;
import com.constellio.data.dao.services.recovery.TransactionLogRecoveryManager;
import com.constellio.data.io.services.facades.IOServices;
import com.constellio.model.services.configs.SystemConfigurationsManager;
import com.constellio.model.services.migrations.ConstellioEIMConfigs;
public class UpgradeAppRecoveryServiceImpl implements UpgradeAppRecoveryService {
private final static Logger LOGGER = LoggerFactory.getLogger(UpgradeAppRecoveryServiceImpl.class);
private static final String WORK_DIR_NAME = UpgradeAppRecoveryServiceImpl.class.getName() + "-settings";
public static long REQUIRED_MEMORY_IN_MO = 200;
public static double REQUIRED_SPACE_IN_GIG = 0.5;
private final TransactionLogRecoveryManager transactionLogRecoveryManager;
private final AppLayerFactory appLayerFactory;
private final SystemPropertiesServices systemPropertiesServices;
private final IOServices ioServices;
private final File oldSetting;
private final ConfigManager configManager;
private final UpgradeAppRecoveryConfigManager upgradeAppRecoveryConfigManager;
public UpgradeAppRecoveryServiceImpl(AppLayerFactory appLayerFactory, IOServices ioServices) {
this.appLayerFactory = appLayerFactory;
this.transactionLogRecoveryManager = appLayerFactory.getModelLayerFactory().getDataLayerFactory()
.getTransactionLogRecoveryManager();
this.ioServices = ioServices;
this.oldSetting = ioServices.newTemporaryFolder(WORK_DIR_NAME);
systemPropertiesServices = new SystemPropertiesServices(appLayerFactory.getModelLayerFactory().getFoldersLocator(),
ioServices);
this.configManager = appLayerFactory.getModelLayerFactory().getDataLayerFactory().getConfigManager();
this.upgradeAppRecoveryConfigManager = new UpgradeAppRecoveryConfigManager(
appLayerFactory.getModelLayerFactory().getDataLayerFactory().getConfigManager());
}
void prepareNextStartup(Throwable exception) {
this.upgradeAppRecoveryConfigManager.onVersionMigratedWithException(exception);
SystemConfigurationsManager systemConfigurationsManager = appLayerFactory.getModelLayerFactory()
.getSystemConfigurationsManager();
systemConfigurationsManager.setValue(ConstellioEIMConfigs.IN_UPDATE_PROCESS, false);
pointToPreviousValidVersion();
}
public void deletePreviousWarCausingFailure() {
//FIXME delete all previous war uploaded and not installed :
// by keeping the list of all uploaded versions (modify UpgradeAppRecoveryConfigManager#onVersionUploadedCorrectly)
String versionCausingPb = this.upgradeAppRecoveryConfigManager.getLastVersionCausingExceptionDirectoryPath();
if (StringUtils.isNotBlank(versionCausingPb)) {
ioServices.deleteQuietly(new File(versionCausingPb));
}
}
@Override
public void startRollbackMode() {
// Synchronized since batch process may be running
synchronized (configManager) {
transactionLogRecoveryManager.startRollbackMode();
saveSettings();
}
}
@Override
public void stopRollbackMode() {
deleteSavedSettings();
transactionLogRecoveryManager.stopRollbackMode();
upgradeAppRecoveryConfigManager.onVersionMigratedCorrectly();
SystemConfigurationsManager systemConfigurationsManager = appLayerFactory.getModelLayerFactory()
.getSystemConfigurationsManager();
systemConfigurationsManager.setValue(ConstellioEIMConfigs.IN_UPDATE_PROCESS, false);
}
@Override
public boolean isInRollbackMode() {
return transactionLogRecoveryManager.isInRollbackMode();
}
public void rollback(Throwable t) {
closeAppAndModelLayers();
replaceSettingsByTheSavedOneButKeepRecoverySettings();
transactionLogRecoveryManager.rollback(t);
prepareNextStartup(t);
deleteSavedSettings();
}
@Override
public UpdateRecoveryImpossibleCause isUpdateWithRecoveryPossible() {
if (this.systemPropertiesServices
.isFreeSpaceInTempFolderLowerThan(getTransactionLogFileSizeInGig() + REQUIRED_SPACE_IN_GIG)) {
return TOO_SHORT_SPACE;
}
if (this.systemPropertiesServices.isAvailableMemoryLowerThan(REQUIRED_MEMORY_IN_MO)) {
return TOO_SHORT_MEMORY;
}
return null;
}
double getTransactionLogFileSizeInGig() {
DataLayerConfiguration configuration = appLayerFactory.getModelLayerFactory()
.getDataLayerFactory().getDataLayerConfiguration();
return this.systemPropertiesServices.getFileSizeInGig(configuration.getSecondTransactionLogBaseFolder());
}
@Override
public void afterWarUpload(ConstellioVersionInfo currentInstalledVersion, ConstellioVersionInfo uploadedVersion) {
this.upgradeAppRecoveryConfigManager.onVersionUploadedCorrectly(currentInstalledVersion, uploadedVersion);
SystemConfigurationsManager systemConfigurationsManager = appLayerFactory.getModelLayerFactory()
.getSystemConfigurationsManager();
systemConfigurationsManager.setValue(ConstellioEIMConfigs.IN_UPDATE_PROCESS, true);
}
@Override
public String getLastUpgradeExceptionMessage() {
return this.upgradeAppRecoveryConfigManager.getUpgradeException();
}
public void close() {
this.transactionLogRecoveryManager.close();
}
private void deleteSavedSettings() {
ioServices.deleteQuietly(this.oldSetting);
}
private void pointToPreviousValidVersion() {
String version = this.upgradeAppRecoveryConfigManager.getLastValidVersion();
String validVersionPath = this.upgradeAppRecoveryConfigManager.getLastValidVersionDirectoryPath();
AppManagementService appService = appLayerFactory.newApplicationService();
appService.pointToVersionDuringApplicationStartup(new ConstellioVersionInfo(version, validVersionPath));
}
void saveSettings() {
File settingFolder = getSettingFolder();
try {
ioServices.copyDirectory(settingFolder, this.oldSetting);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void closeAppAndModelLayers() {
appLayerFactory.close(false);
appLayerFactory.getModelLayerFactory().close(false);
}
private void replaceSettingsByTheSavedOneButKeepRecoverySettings() {
if (this.oldSetting.exists()) {
Map<String, String> currentRecoveryProperties = this.upgradeAppRecoveryConfigManager.getAllProperties();
ConfigManager confManager = appLayerFactory.getModelLayerFactory()
.getDataLayerFactory().getConfigManager();
confManager.copySettingsFrom(this.oldSetting);
this.upgradeAppRecoveryConfigManager.replaceAllProperties(currentRecoveryProperties);
}
}
File getSettingFolder() {
return appLayerFactory.getModelLayerFactory().getDataLayerFactory().getDataLayerConfiguration()
.getSettingsFileSystemBaseFolder();
}
}