Tony CHEMIT pushed to branch develop at ultreiaio / ird-observe Commits: 3c6f6c90 by Tony Chemit at 2020-05-21T14:20:04+02:00 First flush of better local data source error handling at closing, or loading - - - - - 21 changed files: - client-configuration/src/main/config/Client.ini - client-configuration/src/main/i18n/getters/config.getter - client-configuration/src/main/java/fr/ird/observe/client/configuration/ClientConfig.java - client-core/src/main/java/fr/ird/observe/client/datasource/api/ObserveSwingDataSource.java - + client-core/src/main/java/fr/ird/observe/client/util/action/ActionState.java - client-datasource-editor-api/pom.xml - client-datasource-editor-api/src/main/i18n/getters/java.getter - client-datasource-editor-api/src/main/java/fr/ird/observe/client/datasource/editor/MainDataSourceListener.java - client-datasource-editor-api/src/main/java/fr/ird/observe/client/datasource/editor/menu/actions/ChangeStorageAction.java - + client-datasource-editor-api/src/main/java/fr/ird/observe/client/datasource/editor/menu/actions/FeedBackBuilder.java - client-datasource-editor-api/src/main/java/fr/ird/observe/client/datasource/editor/menu/actions/ImportStorageFromFileAction.java - + client-datasource-editor-api/src/main/java/fr/ird/observe/client/datasource/editor/menu/actions/LoadStorageActionSupport.java - + client-datasource-editor-api/src/main/java/fr/ird/observe/client/datasource/editor/menu/actions/LoadingDataSourceContext.java - client-datasource-editor-api/src/main/java/fr/ird/observe/client/datasource/editor/wizard/StorageUIHandler.java - client-datasource-editor-api/src/main/java/fr/ird/observe/client/datasource/editor/wizard/StorageUILauncher.java - client-datasource-editor-api/src/main/java/fr/ird/observe/client/datasource/editor/wizard/StorageUIModel.java - client-datasource-editor-api/src/main/java/fr/ird/observe/client/datasource/editor/wizard/connexion/DataSourceSelectorModel.java - client-runner/src/main/java/fr/ird/observe/client/RunObserve.java - observe-i18n/src/main/i18n/translations/observe_en_GB.properties - observe-i18n/src/main/i18n/translations/observe_es_ES.properties - observe-i18n/src/main/i18n/translations/observe_fr_FR.properties Changes: ===================================== client-configuration/src/main/config/Client.ini ===================================== @@ -82,6 +82,14 @@ defaultValue = ${data.directory}/backup transient = true final = true +[option feedBackDirectory] +description = observe.config.feedBackDirectory.description +key = feedBack.directory +type = file +defaultValue = ${data.directory}/feedback +transient = true +final = true + [option resourcesDirectory] description = observe.config.defaultResourcesDirectory.description key = resources.directory ===================================== client-configuration/src/main/i18n/getters/config.getter ===================================== @@ -34,6 +34,7 @@ observe.config.defaultMapDirectory.description observe.config.defaultReportDirectory.description observe.config.defaultResourcesDirectory.description observe.config.defaultValidationReportDirectory.description +observe.config.feedBackDirectory.description observe.config.h2.can.editReferential.description observe.config.h2.can.migrate.description observe.config.h2.login.description ===================================== client-configuration/src/main/java/fr/ird/observe/client/configuration/ClientConfig.java ===================================== @@ -116,6 +116,7 @@ public class ClientConfig extends GeneratedClientConfig implements TemplateGener private static final Logger log = LogManager.getLogger(ClientConfig.class); /** le pattern du fichier de sauvegarde d'une base locale */ private static final String BACKUP_DB_PATTERN = "observe-v%1$s-%2$tF--%2$tk-%2$tM-%2$tS.sql.gz"; + private static final String FEED_BACK_PATTERN = "observe-v%1$s-feedback-%2$tF--%2$tk-%2$tM-%2$tS.zip"; private static final String APPLICATION_VERSION = "application.version"; private static final String VERSION = "version"; @@ -539,6 +540,9 @@ public class ClientConfig extends GeneratedClientConfig implements TemplateGener public File newBackupDataFile() { return new File(getBackupDirectory(), String.format(BACKUP_DB_PATTERN, getBuildVersion().toString().replaceAll("\\.","_"), new Date())); } + public File newFeedBackFile() { + return new File(getFeedBackDirectory(), String.format(FEED_BACK_PATTERN, getBuildVersion().toString().replaceAll("\\.","_"), new Date())); + } public void saveForUser() { log.info(t("observe.message.save.configuration", get().getUserConfigFile())); ===================================== client-core/src/main/java/fr/ird/observe/client/datasource/api/ObserveSwingDataSource.java ===================================== @@ -25,9 +25,11 @@ package fr.ird.observe.client.datasource.api; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import fr.ird.observe.client.configuration.ClientConfig; +import fr.ird.observe.client.configuration.WithClientConfig; import fr.ird.observe.client.datasource.api.event.ObserveSwingDataSourceEvent; import fr.ird.observe.client.datasource.api.event.ObserveSwingDataSourceListener; import fr.ird.observe.client.datasource.dcp.FloatingObjectPresetsManager; +import fr.ird.observe.client.util.UIHelper; import fr.ird.observe.dto.IdDto; import fr.ird.observe.dto.ObserveUtil; import fr.ird.observe.dto.data.ps.dcp.FloatingObjectPreset; @@ -64,6 +66,8 @@ import fr.ird.observe.services.service.DatabaseOpenException; import fr.ird.observe.services.service.ObserveService; import fr.ird.observe.services.service.referential.ObserveReferentialCache; import fr.ird.observe.services.service.referential.ReferentialService; +import fr.ird.observe.services.service.sql.AddSqlScriptProducerRequest; +import fr.ird.observe.services.service.sql.SqlScriptProducerService; import fr.ird.observe.spi.map.ImmutableSetDtoMap; import fr.ird.observe.spi.map.ImmutableSetStringMap; import fr.ird.observe.spi.map.ImmutableTypedMap; @@ -72,6 +76,7 @@ import io.ultreia.java4all.bean.spi.GenerateJavaBeanDefinition; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.nuiton.jaxx.runtime.swing.SwingUtil; +import org.nuiton.topia.persistence.script.TopiaSqlScript; import org.nuiton.version.Version; import javax.swing.BoundedRangeModel; @@ -80,10 +85,12 @@ import javax.swing.JOptionPane; import javax.swing.event.EventListenerList; import java.io.File; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.util.Iterator; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; @@ -96,7 +103,7 @@ import static io.ultreia.java4all.i18n.I18n.t; */ @GenerateJavaBeanDefinition @GenerateTemplate(template = "dataSourceInformation.ftl") -public class ObserveSwingDataSource extends ObserveServicesProviderSupport { +public class ObserveSwingDataSource extends ObserveServicesProviderSupport implements WithClientConfig { private static final Logger log = LogManager.getLogger(ObserveSwingDataSource.class); @@ -106,6 +113,7 @@ public class ObserveSwingDataSource extends ObserveServicesProviderSupport { private final ObserveReferentialCache referentialCache; private final ObserveServicesProvider servicesProvider; private final Icon icon; + private final ObserveServiceMainFactory serviceFactory; private ObserveDataSourceConnection connection; private BoundedRangeModel progressModel; @@ -116,7 +124,6 @@ public class ObserveSwingDataSource extends ObserveServicesProviderSupport { // indique si l'utilisateur connecté est le propriétaire de la base private boolean owner; private boolean superUser; - private final ObserveServiceMainFactory serviceFactory; public ObserveSwingDataSource(ClientConfig config, ObserveServiceMainFactory serviceFactory, ObserveDataSourceConfiguration configuration) { this.config = config; @@ -357,14 +364,8 @@ public class ObserveSwingDataSource extends ObserveServicesProviderSupport { JOptionPane.WARNING_MESSAGE); if (answer == JOptionPane.YES_OPTION) { - DataSourceService dataSourceService = servicesProvider.getDataSourceService(); - - - if (log.isInfoEnabled()) { - log.info("Migrate data source " + getLabel() + " in " + dbVersion + " to " + targetVersion); - } - + log.info(String.format("Migrate data source %s in %s to %s", getLabel(), dbVersion, targetVersion)); dataSourceService.migrateData(getConfiguration()); } } @@ -457,12 +458,27 @@ public class ObserveSwingDataSource extends ObserveServicesProviderSupport { this.superUser = superUser; } - public void addObserveSwingDataSourceListener(ObserveSwingDataSourceListener listener) { - listenerList.add(ObserveSwingDataSourceListener.class, listener); + /** + * Effectue une sauvegarde de la base locale vers le fichier choisi. + * + * @param dst le fichier de sauvegarde + */ + public void backupLocalDatabase(Path dst) { + Objects.requireNonNull(dst, "file where to backup can not be null"); + log.info(String.format("Do backup of %s into: %s", this, dst)); + try { + Files.deleteIfExists(dst); + AddSqlScriptProducerRequest request = AddSqlScriptProducerRequest.forH2(getClientConfig().getModelVersion()).addSchema().addReferential().addAllData(); + SqlScriptProducerService dumpProducerService = getSqlScriptProducerService(); + TopiaSqlScript dataDump = dumpProducerService.produceAddSqlScript(request); + dataDump.copy(dst); + } catch (Exception e) { + UIHelper.handlingError(e); + } } - private ObserveSwingDataSourceListener[] getObserveSwingDataSourceListener() { - return listenerList.getListeners(ObserveSwingDataSourceListener.class); + public void addObserveSwingDataSourceListener(ObserveSwingDataSourceListener listener) { + listenerList.add(ObserveSwingDataSourceListener.class, listener); } public void removeObserveSwingDataSourceListener(ObserveSwingDataSourceListener listener) { @@ -470,6 +486,10 @@ public class ObserveSwingDataSource extends ObserveServicesProviderSupport { listenerList.remove(ObserveSwingDataSourceListener.class, listener); } + private ObserveSwingDataSourceListener[] getObserveSwingDataSourceListener() { + return listenerList.getListeners(ObserveSwingDataSourceListener.class); + } + private void fireNewMessage(String message) { fireNewMessage(message, ObserveSwingDataSourceEvent.MessageLevel.INFO); } @@ -645,7 +665,7 @@ public class ObserveSwingDataSource extends ObserveServicesProviderSupport { missingIds.removeAll(resultIds); log.warn(String.format("Remove preset: %s (%s id(s) not found in database).", preset.getLabel(config.getReferentialLocale()), missingIds)); } - progressModel.setExtent(1); + progressModel.setValue(progressModel.getValue() + 1); } public void setCanMigrate(ClientConfig appConfig) { ===================================== client-core/src/main/java/fr/ird/observe/client/util/action/ActionState.java ===================================== @@ -0,0 +1,100 @@ +package fr.ird.observe.client.util.action; + +/*- + * #%L + * ObServe :: Client Core + * %% + * Copyright (C) 2008 - 2020 IRD, Code Lutin, Ultreia.io + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +/** + * Describe an atomic action state. + * + * @author Tony Chemit - dev@tchemit.fr + * @since 8.0 + */ +public enum ActionState { + /** + * When action is skipped. + */ + SKIP, + /** + * When action is pending. + */ + PENDING, + /** + * When action is done. + */ + DONE, + /** + * When action has failed. + */ + FAILED; + + public boolean notSkip() { + return this != SKIP; + } + + public boolean skip() { + return this == SKIP; + } + + public boolean success() { + return this == DONE; + } + + public boolean fail() { + return this == FAILED; + } + + public boolean pending() { + return this == PENDING; + } + + public static ActionState failIfPreviousFail(ActionState initialState, ActionState... previousStates) { + if (initialState.skip()) { + // If we are on skip state, then nothing to do, so previous states are not used + return initialState; + } + for (ActionState previousState : previousStates) { + if (previousState.fail()) { + // One of previous state has failed, so this will automatic failed + return FAILED; + } + } + // Nothing was changed from previous state + return initialState; + } + + public static ActionState pendingIfPreviousFail(ActionState initialState, ActionState... previousStates) { + if (initialState.pending()) { + // If we are on pending state, then already mark to do it, so previous states are not used + return initialState; + } + for (ActionState previousState : previousStates) { + if (previousState.fail()) { + // One of previous state has failed, so this will automatic failed + return PENDING; + } + } + // Nothing was changed from previous state + return initialState; + } + + +} ===================================== client-datasource-editor-api/pom.xml ===================================== @@ -86,6 +86,11 @@ <artifactId>persistence</artifactId> </dependency> + <dependency> + <groupId>io.ultreia.java4all.config</groupId> + <artifactId>config-api</artifactId> + </dependency> + <dependency> <groupId>io.ultreia.java4all</groupId> <artifactId>application-context</artifactId> ===================================== client-datasource-editor-api/src/main/i18n/getters/java.getter ===================================== @@ -85,6 +85,7 @@ observe.action.test.server.tip observe.choice.cancel observe.choice.continue observe.choice.doNotSave +observe.choice.generateFeedBack observe.choice.quit observe.choice.replace observe.choice.save @@ -137,7 +138,10 @@ observe.entity.message.creating observe.entity.message.reading observe.entity.message.updating observe.error.storage.could.not.backup.unsane.local.db +observe.error.storage.could.not.close.local.data.source.message +observe.error.storage.could.not.close.local.data.source.title observe.error.storage.could.not.load.local.db +observe.error.storage.could.not.open.local.data.source.title observe.menu.navigation.action.noAction observe.message.db.closed observe.message.db.loaded ===================================== client-datasource-editor-api/src/main/java/fr/ird/observe/client/datasource/editor/MainDataSourceListener.java ===================================== @@ -42,13 +42,11 @@ import static io.ultreia.java4all.i18n.I18n.t; class MainDataSourceListener extends ObserveSwingDataSourceListenerAdapter implements WithObserveDataSourcesManager, WithClientConfig, WithClientUIContext { private final ObserveDataSourcesManager dataSourcesManager; - private final ClientConfig config; private final DataSourceEditorBodyContent bodyContent; public MainDataSourceListener(DataSourceEditorBodyContent bodyContent) { this.bodyContent = bodyContent; this.dataSourcesManager = getObserveDataSourcesManager(); - this.config = getClientConfig(); } @Override @@ -60,9 +58,6 @@ class MainDataSourceListener extends ObserveSwingDataSourceListenerAdapter imple @Override public void onOpened(ObserveSwingDataSourceEvent event) { ObserveSwingDataSource source = event.getSource(); - if (source.isLocal()) { - config.setLocalStorageExist(true); - } bodyContent.setDataSource(source); } ===================================== client-datasource-editor-api/src/main/java/fr/ird/observe/client/datasource/editor/menu/actions/ChangeStorageAction.java ===================================== @@ -23,14 +23,18 @@ package fr.ird.observe.client.datasource.editor.menu.actions; */ import fr.ird.observe.client.constants.DbMode; -import fr.ird.observe.client.datasource.editor.menu.DataSourceEditorMenu; +import fr.ird.observe.client.datasource.api.ObserveDataSourcesManagerApplicationComponent; +import fr.ird.observe.client.datasource.api.ObserveSwingDataSource; +import fr.ird.observe.client.datasource.editor.wizard.StorageStep; +import fr.ird.observe.client.datasource.editor.wizard.StorageUI; import fr.ird.observe.client.datasource.editor.wizard.StorageUILauncher; +import fr.ird.observe.client.datasource.editor.wizard.StorageUIModel; import fr.ird.observe.client.main.ObserveMainUI; -import fr.ird.observe.client.main.body.NoBodyContentComponent; +import org.apache.commons.collections4.CollectionUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.awt.event.ActionEvent; +import java.util.Arrays; import java.util.EnumSet; import java.util.Set; import java.util.stream.Collectors; @@ -43,7 +47,7 @@ import static io.ultreia.java4all.i18n.I18n.t; * @author Tony Chemit - dev@tchemit.fr * @since 3.13 */ -public class ChangeStorageAction extends DataSourceEditorMenuActionSupport implements Runnable { +public class ChangeStorageAction extends LoadStorageActionSupport { private static final Logger log = LogManager.getLogger(ChangeStorageAction.class); @@ -54,12 +58,6 @@ public class ChangeStorageAction extends DataSourceEditorMenuActionSupport imple this(null, null); } - public ChangeStorageAction(String actionName, char keyStroke) { - super(actionName, t("observe.action.change.storage"), t("observe.action.change.storage.tip"), "db-change", keyStroke); - this.dbModes = EnumSet.noneOf(DbMode.class); - this.title = null; - } - public ChangeStorageAction(Set<DbMode> dbModes, String title) { super(t("observe.action.change.storage"), t("observe.action.change.storage.tip"), "db-change", 'C'); this.dbModes = dbModes == null ? EnumSet.noneOf(DbMode.class) : dbModes; @@ -67,21 +65,47 @@ public class ChangeStorageAction extends DataSourceEditorMenuActionSupport imple } @Override - protected void doActionPerformed(ActionEvent event, DataSourceEditorMenu ui) { - run(); + protected String getTitle() { + return t(title); } @Override - public void run() { + protected void init(StorageUI ui) { + log.debug(String.format("Incoming db mode : %s", dbModes.stream().map(DbMode::name).collect(Collectors.joining(", ")))); + StorageUIModel model = ui.getModel(); + if (CollectionUtils.isNotEmpty(dbModes)) { + log.info(String.format("will use incoming mode %s", dbModes.stream().map(DbMode::name).collect(Collectors.joining(", ")))); + + model.setExcludeSteps(Arrays.asList(StorageStep.SELECT_DATA, + StorageStep.BACKUP, + StorageStep.CONFIG_REFERENTIAL, + StorageStep.CONFIG_DATA, + StorageStep.ROLES)); + model.setCanCreateLocalService(dbModes.contains(DbMode.CREATE_LOCAL)); + model.setCanUseLocalService(dbModes.contains(DbMode.USE_LOCAL)); + model.setCanUseRemoteService(dbModes.contains(DbMode.USE_REMOTE)); + model.setCanUseServerService(dbModes.contains(DbMode.USE_SERVER)); - ObserveMainUI mainUI = getClientUIContext().getMainUI(); - boolean canContinue = mainUI.changeBodyContent(NoBodyContentComponent.class); -// boolean canContinue = getContentUIManager().closeSelectedContentUI(); - if (canContinue) { - log.info("Start change storage with dbMode: " + dbModes.stream().map(DbMode::name).collect(Collectors.joining(", "))); - StorageUILauncher.changeStorage(ui == null ? mainUI : ui, mainUI, dbModes, t(title)); + model.updateUniverse(); + model.setDbMode(dbModes.stream().findFirst().orElse(null)); + } else { + boolean localOpened = ObserveDataSourcesManagerApplicationComponent.value().getOptionalMainDataSource().map(ObserveSwingDataSource::isLocal).orElse(false); + if (localOpened) { + log.debug("Can not use local db (already opened)"); + model.setCanUseLocalService(false); + } + model.setCanCreateLocalService(true); + model.setCanUseRemoteService(true); + model.setCanUseServerService(true); } + model.updateUniverse(); + } + @Override + protected StorageUILauncher startWizard(ObserveMainUI mainUI) { + log.info(String.format("Start change storage with dbMode: %s", dbModes.stream().map(DbMode::name).collect(Collectors.joining(", ")))); + return super.startWizard(mainUI); } + } ===================================== client-datasource-editor-api/src/main/java/fr/ird/observe/client/datasource/editor/menu/actions/FeedBackBuilder.java ===================================== @@ -0,0 +1,144 @@ +package fr.ird.observe.client.datasource.editor.menu.actions; + +/*- + * #%L + * ObServe :: Client DataSource Editor API + * %% + * Copyright (C) 2008 - 2020 IRD, Code Lutin, Ultreia.io + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import fr.ird.observe.client.ClientUIContextApplicationComponent; +import fr.ird.observe.client.configuration.ClientConfig; +import fr.ird.observe.client.util.UIHelper; +import fr.ird.observe.client.util.action.ActionState; +import org.apache.commons.io.FileUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.swing.JOptionPane; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import static io.ultreia.java4all.i18n.I18n.t; + +public class FeedBackBuilder { + + private static final Logger log = LogManager.getLogger(FeedBackBuilder.class); + + private final LoadingDataSourceContext loadingContext; + private final ClientConfig config; + private final File feedBackFile; + + public static void onClosingLocalDataSource(LoadingDataSourceContext loadingContext) { + new FeedBackBuilder(loadingContext).onClosingLocalDataSource(); + } + + public static void onLoadingLocalDataSource(LoadingDataSourceContext loadingContext) { + new FeedBackBuilder(loadingContext).onLoadingLocalDataSource(); + } + + public FeedBackBuilder(LoadingDataSourceContext loadingContext) { + this.loadingContext = Objects.requireNonNull(loadingContext); + this.config = loadingContext.getConfig(); + this.feedBackFile = config.newFeedBackFile(); + } + + private void onClosingLocalDataSource() { + ActionState actionState = onLocalDataSourceError(t("observe.error.storage.could.not.close.local.data.source.title", feedBackFile)); + loadingContext.setFeedBackOnClosingLocalDatasourceState(actionState); + + } + + private void onLoadingLocalDataSource() { + ActionState actionState = onLocalDataSourceError(t("observe.error.storage.could.not.open.local.data.source.title", feedBackFile)); + loadingContext.setFeedBackOnClosingLocalDatasourceState(actionState); + + } + + + private ActionState onLocalDataSourceError(String title) { + + int response = UIHelper.askUser(ClientUIContextApplicationComponent.value().getMainUI(), title, t("observe.error.storage.could.not.close.local.data.source.message"), JOptionPane.ERROR_MESSAGE, + new Object[]{t("observe.choice.generateFeedBack"), t("observe.choice.cancel")}, 1); + + + switch (response) { + case JOptionPane.CLOSED_OPTION: + case 1: + // cancel operation + return ActionState.FAILED; + } + log.info(String.format("Create feed back at: %s", feedBackFile)); + try { + createFeedBack(); + setLocalStorageDoesNotExist(); + destroyLocalDataSource(); + loadingContext.setFeedBackOnLoadingLocalDatasourceState(ActionState.DONE); + } catch (IOException e) { + log.error("Could not create feed back", e); + return ActionState.FAILED; + } + return ActionState.DONE; + } + + + private void createFeedBack() throws IOException { + if (Files.notExists(feedBackFile.toPath())) { + Files.createDirectories(feedBackFile.toPath()); + } + try (ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(feedBackFile))) { + copyDirectoryToZip(config.getDbDirectory().toPath(), outputStream); + copyDirectoryToZip(config.getResourcesDirectory().toPath(), outputStream); + Path userConfigFile = config.get().getUserConfigFile().toPath(); + copyFileToZip(userConfigFile.getParent(), userConfigFile, outputStream); + } + } + + private void destroyLocalDataSource() throws IOException { + File dataSourceDirectory = config.getDbDirectory(); + FileUtils.cleanDirectory(dataSourceDirectory); + } + + private void copyDirectoryToZip(Path rootFile, ZipOutputStream zipOutputStream) throws IOException { + Files.walk(rootFile).filter(Files::isRegularFile).forEach(p -> copyFileToZip(rootFile, p, zipOutputStream)); + } + + private void copyFileToZip(Path rootFile, Path file, ZipOutputStream zipOutputStream) { + + String entryName = file.relativize(rootFile).toString(); + try { + zipOutputStream.putNextEntry(new ZipEntry(entryName)); + Files.copy(file, zipOutputStream); + } catch (IOException e) { + log.error(String.format("Could not copy file %s to zip entry %S", file, entryName)); + } + + } + + private void setLocalStorageDoesNotExist() { + config.setLocalStorageExist(false); + config.saveForUser(); + } + +} ===================================== client-datasource-editor-api/src/main/java/fr/ird/observe/client/datasource/editor/menu/actions/ImportStorageFromFileAction.java ===================================== @@ -25,17 +25,14 @@ package fr.ird.observe.client.datasource.editor.menu.actions; import fr.ird.observe.client.configuration.ClientConfig; import fr.ird.observe.client.constants.CreationMode; import fr.ird.observe.client.constants.DbMode; -import fr.ird.observe.client.datasource.editor.menu.DataSourceEditorMenu; import fr.ird.observe.client.datasource.editor.wizard.StorageStep; import fr.ird.observe.client.datasource.editor.wizard.StorageUI; -import fr.ird.observe.client.datasource.editor.wizard.StorageUIHandler; import fr.ird.observe.client.datasource.editor.wizard.StorageUILauncher; import fr.ird.observe.client.datasource.editor.wizard.StorageUIModel; import fr.ird.observe.client.main.ObserveMainUI; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.awt.event.ActionEvent; import java.io.File; import static io.ultreia.java4all.i18n.I18n.t; @@ -46,69 +43,55 @@ import static io.ultreia.java4all.i18n.I18n.t; * @author Tony Chemit - dev@tchemit.fr * @since 3.13 */ -public class ImportStorageFromFileAction extends DataSourceEditorMenuActionSupport { +public class ImportStorageFromFileAction extends LoadStorageActionSupport { private static final Logger log = LogManager.getLogger(ImportStorageFromFileAction.class); + public ImportStorageFromFileAction() { super(t("observe.action.load.from.file"), t("observe.action.load.from.file.tip"), "local-import", 'I'); } @Override - protected void doActionPerformed(ActionEvent event, DataSourceEditorMenu ui) { - - final ObserveMainUI mainUI = getClientUIContext().getMainUI(); - boolean canContinue = mainUI.canChangeBodyContent(); - if (canContinue) { - - - if (getObserveDataSourcesManager().getOptionalMainDataSource().isPresent()) { - log.info("Closing datasource..."); - getDataSourceEditorBodyContent().doCloseStorage(); - } - - new StorageUILauncher(ui, mainUI, t("observe.title.import.localDB")) { - @Override - protected void init(StorageUI ui) { - super.init(ui); - StorageUIModel model = ui.getModel(); - - model.setDumpFile(getClientConfig().getImportDirectory()); - - model.setCanCreateLocalService(true); - model.setCanUseLocalService(false); - model.setCanUseRemoteService(false); - model.setCanUseServerService(false); - model.setDbMode(DbMode.CREATE_LOCAL); - model.setCreationMode(CreationMode.IMPORT_EXTERNAL_DUMP); - if (model.isLocalStorageExist()) { - // sauvegarde base locale possible - model.setSteps(StorageStep.CONFIG, StorageStep.BACKUP, StorageStep.CONFIRM); - - // et requise par défaut - model.setDoBackup(true); - } else { - model.setSteps(StorageStep.CONFIG, StorageStep.CONFIRM); - } - } - - @Override - public void doAction(StorageUI ui) { - - log.info("Start importing new datasource from backup file..."); - - super.doAction(ui); - StorageUIHandler handler = ui.getHandler(); - handler.doChangeStorage(ui.getModel()); - - File importDirectory = ui.getModel().getDumpFile().getParentFile(); - ClientConfig config = getClientConfig(); - config.setImportDirectory(importDirectory); - config.saveForUser(); - } + protected String getTitle() { + return t("observe.title.import.localDB"); + } - }.start(); + @Override + protected void init(StorageUI ui) { + StorageUIModel model = ui.getModel(); + + model.setDumpFile(getClientConfig().getImportDirectory()); + + model.setCanCreateLocalService(true); + model.setCanUseLocalService(false); + model.setCanUseRemoteService(false); + model.setCanUseServerService(false); + model.setDbMode(DbMode.CREATE_LOCAL); + model.setCreationMode(CreationMode.IMPORT_EXTERNAL_DUMP); + if (model.isLocalStorageExist()) { + // sauvegarde base locale possible + model.setSteps(StorageStep.CONFIG, StorageStep.BACKUP, StorageStep.CONFIRM); + + // et requise par défaut + model.setDoBackup(true); + } else { + model.setSteps(StorageStep.CONFIG, StorageStep.CONFIRM); } + } + @Override + protected StorageUILauncher startWizard(ObserveMainUI mainUI) { + if (getObserveDataSourcesManager().getOptionalMainDataSource().isPresent()) { + log.info("Closing datasource..."); + getDataSourceEditorBodyContent().doCloseStorage(); + } + return super.startWizard(mainUI); } + @Override + protected void doChangeStorage(StorageUI ui) { + log.info("Start importing new datasource from backup file..."); + + super.doChangeStorage(ui); + } } ===================================== client-datasource-editor-api/src/main/java/fr/ird/observe/client/datasource/editor/menu/actions/LoadStorageActionSupport.java ===================================== @@ -0,0 +1,369 @@ +package fr.ird.observe.client.datasource.editor.menu.actions; + +/*- + * #%L + * ObServe :: Client DataSource Editor API + * %% + * Copyright (C) 2008 - 2020 IRD, Code Lutin, Ultreia.io + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import fr.ird.observe.client.configuration.ClientConfig; +import fr.ird.observe.client.constants.DbMode; +import fr.ird.observe.client.datasource.api.ObserveDataSourcesManager; +import fr.ird.observe.client.datasource.api.ObserveSwingDataSource; +import fr.ird.observe.client.datasource.dcp.WithFloatingObjectPresetsManager; +import fr.ird.observe.client.datasource.editor.DataSourceEditor; +import fr.ird.observe.client.datasource.editor.DataSourceEditorBodyContent; +import fr.ird.observe.client.datasource.editor.menu.DataSourceEditorMenu; +import fr.ird.observe.client.datasource.editor.wizard.StorageUI; +import fr.ird.observe.client.datasource.editor.wizard.StorageUILauncher; +import fr.ird.observe.client.datasource.editor.wizard.StorageUIModel; +import fr.ird.observe.client.main.ObserveMainUI; +import fr.ird.observe.client.main.body.NoBodyContentComponent; +import fr.ird.observe.client.util.ProgressModel; +import fr.ird.observe.client.util.action.ActionState; +import fr.ird.observe.dto.ObserveUtil; +import fr.ird.observe.services.configuration.DataSourceCreateConfigurationDto; +import fr.ird.observe.services.configuration.ObserveDataSourceInformation; +import fr.ird.observe.services.configuration.topia.ObserveDataSourceConfigurationTopiaH2; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.nuiton.jaxx.runtime.JAXXContext; + +import java.awt.event.ActionEvent; +import java.nio.file.Path; +import java.util.Objects; + +import static io.ultreia.java4all.i18n.I18n.t; + +/** + * @author Tony Chemit - dev@tchemit.fr + * @since 8.0 + */ +public abstract class LoadStorageActionSupport extends DataSourceEditorMenuActionSupport implements Runnable, WithFloatingObjectPresetsManager { + + private static final Logger log = LogManager.getLogger(LoadStorageActionSupport.class); + + protected abstract String getTitle(); + + protected abstract void init(StorageUI ui); + + public LoadStorageActionSupport(String label, String shortDescription, String actionIcon, char acceleratorKey) { + super(label, shortDescription, actionIcon, acceleratorKey); + } + + @Override + protected void doActionPerformed(ActionEvent event, DataSourceEditorMenu ui) { + run(); + } + + @Override + public void run() { + + ObserveMainUI mainUI = getClientUIContext().getMainUI(); + boolean canContinue = mainUI.changeBodyContent(NoBodyContentComponent.class); + if (!canContinue) { + return; + } + startWizard(mainUI).start(); + + } + + protected StorageUILauncher startWizard(ObserveMainUI mainUI) { + JAXXContext rootContext = ui == null ? mainUI : ui; + return new StorageUILauncher(rootContext, mainUI, getTitle()) { + + @Override + protected void init(StorageUI ui) { + super.init(ui); + LoadStorageActionSupport.this.init(ui); + } + + @Override + public void doAction(StorageUI ui) { + super.doAction(ui); + doChangeStorage(ui); + } + + }; + } + + protected void doChangeStorage(StorageUI ui) { + + StorageUIModel model = ui.getModel(); + + ProgressModel progressModel = model.getProgressModel(); + progressModel.installUI(ui.getCONFIRM().getProgressBar()); + + LoadingDataSourceContext loadingContext = new LoadingDataSourceContext(model, + getClientConfig(), + getObserveDataSourcesManager().getMainDataSource(), + getFloatingObjectPresetsManager()); + + doOpenLocalDataSource(loadingContext); + + doBackupLocalDataSource(loadingContext); + + doDestroyLocalDataSource(loadingContext); + + doCloseLocalDatasource(loadingContext); + + doCloseCurrentDatasource(loadingContext); + + doFeedBackOnLocalDataSourceClosingErrors(loadingContext); + + try { + ObserveMainUI mainUI = getClientUIContext().getMainUI(); + doOpenNewDataSource(mainUI, loadingContext); + mainUI.changeBodyContent(DataSourceEditor.class); + } catch (Exception e) { + doFeedBackOnLocalDataSourceOnLoadingErrors(loadingContext); + } finally { + ObserveUtil.cleanMemory(); + } + + } + + protected void doOpenLocalDataSource(LoadingDataSourceContext loadingContext) { + + ObserveSwingDataSource currentDataSource = loadingContext.getCurrentDataSource(); + boolean doBackup = loadingContext.getBackupState().notSkip(); + boolean destroyLocalBase = loadingContext.getDestroyLocalDatabaseState().notSkip(); + + ObserveSwingDataSource localDataSource = null == currentDataSource || !currentDataSource.isLocal() ? null : currentDataSource; + if (destroyLocalBase || doBackup) { + if (localDataSource == null) { + + ObserveDataSourcesManager dataSourcesManager = getObserveDataSourcesManager(); + + ObserveDataSourceConfigurationTopiaH2 localConfiguration = dataSourcesManager.newH2DataSourceConfiguration(t("observe.runner.initStorage.label.local")); + + // can't migrate datasource in this case + localConfiguration.setCanMigrate(false); + + //do open local datasource + localDataSource = dataSourcesManager.newDataSource(localConfiguration); + } + + Objects.requireNonNull(localDataSource); + + // Let's check if the datasource is opened or not : + // we could have close the datasource through the ui + // If the datasource is closed, we try to open it + if (!localDataSource.isOpen()) { + try { + + localDataSource.open(); + + } catch (Exception e) { + log.error(t("observe.error.storage.could.not.load.local.db", e.getMessage()), e); + + // set no local data source, will then trigger some closing feedback + localDataSource = null; + } + } + } + loadingContext.incrementsProgress(); + loadingContext.setLocalDataSource(localDataSource); + } + + private void doBackupLocalDataSource(LoadingDataSourceContext loadingContext) { + ActionState result = loadingContext.getBackupPendingState(); + if (result.pending()) { + ObserveSwingDataSource localDataSource = loadingContext.getLocalDataSource(); + if (localDataSource == null) { + // local database is not sane, can't do backup + log.error(t("observe.error.storage.could.not.backup.unsane.local.db")); + result = ActionState.FAILED; + } else { + StorageUIModel model = loadingContext.getModel(); + Path f = model.getBackupFile().toPath(); + log.info(String.format("Do backup with %s in %s", localDataSource, f)); + try { + localDataSource.backupLocalDatabase(f); + result = ActionState.DONE; + } catch (Exception e) { + result = ActionState.FAILED; + log.error("Could not backup local datasource", e); + } + } + } + loadingContext.incrementsProgress(); + loadingContext.setBackupState(result); + } + + private void doDestroyLocalDataSource(LoadingDataSourceContext loadingContext) { + ActionState result = loadingContext.getDestroyLocalDatabasePendingState(); + if (result.pending()) { + ObserveSwingDataSource localDataSource = loadingContext.getLocalDataSource(); + if (localDataSource == null) { + // could not load local data source, mark this step as failed + result = ActionState.FAILED; + } else { + log.info(String.format("Destroy local data source %s", localDataSource)); + try { + localDataSource.destroy(); + result = ActionState.DONE; + } catch (Exception e) { + log.error("Could not destroy local data source", e); + result = ActionState.FAILED; + } + } + } + loadingContext.incrementsProgress(); + loadingContext.setDestroyLocalDatabaseState(result); + } + + private void doCloseLocalDatasource(LoadingDataSourceContext loadingContext) { + loadingContext.setCloseLocalDatabaseState(doCloseDatasource(loadingContext, loadingContext.getCloseLocalDatabasePendingState(), loadingContext.getLocalDataSource())); + } + + private void doCloseCurrentDatasource(LoadingDataSourceContext loadingContext) { + loadingContext.setCloseCurrentDatabaseState(doCloseDatasource(loadingContext, loadingContext.getCloseCurrentDatabasePendingState(), loadingContext.getCurrentDataSource())); + } + + private void doOpenNewDataSource(ObserveMainUI mainUI, LoadingDataSourceContext loadingContext) { + if (loadingContext.getOpenDataSourcePendingState().skip()) { + loadingContext.setOpenDataSourceState(ActionState.FAILED); + } + StorageUIModel model = loadingContext.getModel(); + ClientConfig config = loadingContext.getConfig(); + ObserveDataSourcesManager dataSourcesManager = getObserveDataSourcesManager(); + + ObserveSwingDataSource newDataSource = model.initDataSourceFromModel(config, dataSourcesManager); + newDataSource.setProgressModel(loadingContext.getProgressModel()); + + boolean useLocalDataSource = model.getDbMode() == DbMode.USE_LOCAL; + + if (useLocalDataSource) { + // try an explicit data source migration + + ObserveDataSourceInformation dataSourceInformation; + try { + dataSourceInformation = newDataSource.checkCanConnect(false); + newDataSource.setOwner(dataSourceInformation.isOwner()); + newDataSource.setSuperUser(dataSourceInformation.isSuperUser()); + } catch (Exception e) { + log.error(String.format("could not get local data source information: %s", newDataSource), e); + loadingContext.setOpenDataSourceState(ActionState.FAILED); + return; + } finally { + loadingContext.incrementsProgress(); + } + + try { + newDataSource.migrateData(dataSourceInformation, config.getModelVersion()); + } catch (Exception e) { + log.error(String.format("could not migrate local data source: %s", newDataSource), e); + loadingContext.setOpenDataSourceState(ActionState.FAILED); + return; + } finally { + loadingContext.incrementsProgress(); + } + } + mainUI.getMainUIBodyContentManager().getBodyTyped(DataSourceEditor.class, DataSourceEditorBodyContent.class).prepareMainStorage(newDataSource, true); + + boolean createLocalDataSource = model.getDbMode() == DbMode.CREATE_LOCAL; + + try { + if (createLocalDataSource) { + // do create local data source + DataSourceCreateConfigurationDto creationConfigurationDto = model.getCreationConfigurationDto(); + newDataSource.create(creationConfigurationDto); + } else { + // open data source + newDataSource.open(); + } + log.info(String.format("Main storage opened %s", newDataSource.getLabel())); + } catch (Exception e) { + log.error(String.format("could not open data source: %s", newDataSource), e); + dataSourcesManager.setMainDataSource(null); + loadingContext.setOpenDataSourceState(ActionState.FAILED); + } finally { + loadingContext.incrementsProgress(); + } + + loadingContext.setOpenDataSourceState(ActionState.DONE); + } + + private ActionState doCloseDatasource(LoadingDataSourceContext loadingContext, ActionState dataSourceState, ObserveSwingDataSource dataSource) { + if (dataSourceState.notSkip()) { + if (dataSource == null || !dataSource.isOpen()) { + dataSourceState = ActionState.DONE; + } else { + log.info(String.format("close data source: %s", dataSource)); + try { + dataSource.close(); + dataSourceState = ActionState.DONE; + } catch (Exception e) { + log.error("Could not close local data source", e); + dataSourceState = ActionState.FAILED; + } + } + } + loadingContext.incrementsProgress(); + return dataSourceState; + } + + private void doFeedBackOnLocalDataSourceClosingErrors(LoadingDataSourceContext loadingContext) { + ActionState result = loadingContext.getFeedBackOnClosingLocalDatasourcePendingState(); + + if (result.notSkip()) { + + //TODO + log.info("Do feed back after errors on closing local data source."); + + try { + + FeedBackBuilder.onClosingLocalDataSource(loadingContext); + result = ActionState.DONE; + } catch (Exception e) { + log.error("Could not feed back", e); + result = ActionState.FAILED; + } + loadingContext.setFeedBackOnClosingLocalDatasourceState(result); + } + + loadingContext.incrementsProgress(); + } + + protected void doFeedBackOnLocalDataSourceOnLoadingErrors(LoadingDataSourceContext loadingContext) { + + ActionState result = loadingContext.getFeedBackOnLoadingLocalDatasourcePendingState(); + if (result.notSkip()) { + + //TODO + log.info("Do feed back after errors on opening local data source."); + + loadingContext.getConfig().setLocalStorageExist(false); + loadingContext.getConfig().saveForUser(); + try { + + result = ActionState.DONE; + } catch (Exception e) { + log.error("Could not feed back", e); + result = ActionState.FAILED; + } + loadingContext.setFeedBackOnLoadingLocalDatasourceState(result); + } + + + loadingContext.incrementsProgress(); + } + +} ===================================== client-datasource-editor-api/src/main/java/fr/ird/observe/client/datasource/editor/menu/actions/LoadingDataSourceContext.java ===================================== @@ -0,0 +1,240 @@ +package fr.ird.observe.client.datasource.editor.menu.actions; + +/*- + * #%L + * ObServe :: Client DataSource Editor API + * %% + * Copyright (C) 2008 - 2020 IRD, Code Lutin, Ultreia.io + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import fr.ird.observe.client.configuration.ClientConfig; +import fr.ird.observe.client.constants.DbMode; +import fr.ird.observe.client.datasource.api.ObserveSwingDataSource; +import fr.ird.observe.client.datasource.dcp.FloatingObjectPresetsManager; +import fr.ird.observe.client.datasource.editor.wizard.StorageUIModel; +import fr.ird.observe.client.util.ProgressModel; +import fr.ird.observe.client.util.action.ActionState; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * @author Tony Chemit - dev@tchemit.fr + * @since 8.0.0 + */ +public class LoadingDataSourceContext { + + private static final Logger log = LogManager.getLogger(LoadingDataSourceContext.class); + + private final StorageUIModel model; + private final ClientConfig config; + private final ObserveSwingDataSource currentDataSource; + private final ProgressModel progressModel; + private ObserveSwingDataSource localDataSource; + private ActionState backupState; + private ActionState destroyLocalDatabaseState; + private ActionState closeLocalDatabaseState; + private ActionState closeCurrentDatabaseState; + private ActionState feedBackOnClosingLocalDatasourceState; + private ActionState openDataSourceState; + private ActionState feedBackOnLoadingLocalDatasourceState; + + LoadingDataSourceContext(StorageUIModel model, ClientConfig config, ObserveSwingDataSource currentDataSource, FloatingObjectPresetsManager floatingObjectPresetsManager) { + this.model = model; + this.config = config; + this.currentDataSource = currentDataSource; + boolean localStorageExist = config.isLocalStorageExist(); + boolean currentDataSourceIsLocal = currentDataSource != null && currentDataSource.isLocal(); + this.backupState = model.isDoBackup() ? ActionState.PENDING : ActionState.SKIP; + this.destroyLocalDatabaseState = localStorageExist && DbMode.CREATE_LOCAL == model.getDbMode() ? ActionState.PENDING : ActionState.SKIP; + + log.info(String.format(" Will backup local db ? %s", backupState.notSkip())); + log.info(String.format(" Will destroy local db ? %s", destroyLocalDatabaseState.notSkip())); + + this.progressModel = model.getProgressModel(); + int stepsCount = computeStepCount(floatingObjectPresetsManager); + + log.info(String.format("Found %d steps to load new data source.", stepsCount)); + + this.progressModel.setValue(0); + this.progressModel.setMaximum(stepsCount); + + this.closeLocalDatabaseState = backupState.notSkip() || destroyLocalDatabaseState.notSkip() || currentDataSourceIsLocal ? ActionState.PENDING : ActionState.SKIP; + this.closeCurrentDatabaseState = currentDataSourceIsLocal ? ActionState.SKIP : ActionState.PENDING; + this.feedBackOnClosingLocalDatasourceState = ActionState.SKIP; + this.feedBackOnLoadingLocalDatasourceState = ActionState.SKIP; + this.openDataSourceState = ActionState.PENDING; + } + + public ActionState getBackupPendingState() { + return backupState; + } + + public ActionState getDestroyLocalDatabasePendingState() { + return ActionState.failIfPreviousFail(getDestroyLocalDatabaseState(), getBackupState()); + } + + public ActionState getCloseLocalDatabasePendingState() { + return ActionState.failIfPreviousFail(getCloseLocalDatabaseState(), getBackupState(), getDestroyLocalDatabaseState()); + } + + public ActionState getCloseCurrentDatabasePendingState() { + return getCloseCurrentDatabaseState(); + } + + public ActionState getFeedBackOnClosingLocalDatasourcePendingState() { + return ActionState.pendingIfPreviousFail(getFeedBackOnClosingLocalDatasourceState(), getBackupState(), getDestroyLocalDatabaseState(), getCloseCurrentDatabaseState()); + } + + public ActionState getFeedBackOnLoadingLocalDatasourcePendingState() { + return ActionState.pendingIfPreviousFail(getOpenDataSourceState()); + } + + public void incrementsProgress() { + progressModel.setValue(progressModel.getValue() + 1); + } + + public StorageUIModel getModel() { + return model; + } + + public ClientConfig getConfig() { + return config; + } + + public ObserveSwingDataSource getCurrentDataSource() { + return currentDataSource; + } + + public ActionState getBackupState() { + return backupState; + } + + public ActionState getDestroyLocalDatabaseState() { + return destroyLocalDatabaseState; + } + + public ProgressModel getProgressModel() { + return progressModel; + } + + public ActionState getCloseLocalDatabaseState() { + return closeLocalDatabaseState; + } + + public ActionState getCloseCurrentDatabaseState() { + return closeCurrentDatabaseState; + } + + public ActionState getOpenDataSourceState() { + return openDataSourceState; + } + + public ActionState getOpenDataSourcePendingState() { + return openDataSourceState; + } + + public void setOpenDataSourceState(ActionState openDataSourceState) { + this.openDataSourceState = openDataSourceState; + } + + public ObserveSwingDataSource getLocalDataSource() { + return localDataSource; + } + + public void setLocalDataSource(ObserveSwingDataSource localDataSource) { + this.localDataSource = localDataSource; + } + + public ActionState getFeedBackOnClosingLocalDatasourceState() { + return feedBackOnClosingLocalDatasourceState; + } + + public ActionState getFeedBackOnLoadingLocalDatasourceState() { + return feedBackOnLoadingLocalDatasourceState; + } + + public void setBackupState(ActionState backupState) { + this.backupState = backupState; + } + + public void setCloseLocalDatabaseState(ActionState closeLocalDatabaseState) { + this.closeLocalDatabaseState = closeLocalDatabaseState; + } + + public void setCloseCurrentDatabaseState(ActionState closeCurrentDatabaseState) { + this.closeCurrentDatabaseState = closeCurrentDatabaseState; + } + + public void setFeedBackOnClosingLocalDatasourceState(ActionState feedBackOnClosingLocalDatasourceState) { + this.feedBackOnClosingLocalDatasourceState = feedBackOnClosingLocalDatasourceState; + } + + public void setFeedBackOnLoadingLocalDatasourceState(ActionState feedBackOnLoadingLocalDatasourceState) { + this.feedBackOnLoadingLocalDatasourceState = feedBackOnLoadingLocalDatasourceState; + } + + public void setDestroyLocalDatabaseState(ActionState destroyLocalDatabaseState) { + this.destroyLocalDatabaseState = destroyLocalDatabaseState; + } + + private int computeStepCount(FloatingObjectPresetsManager floatingObjectPresetsManager) { + DbMode dbMode = model.getDbMode(); + int stepsCount = 0; + + // open local + stepsCount++; + + // backup local + stepsCount++; + + // destroy local + stepsCount++; + + // close local + stepsCount++; + + // close current + stepsCount++; + + // feed back on closing + stepsCount++; + + // open new data source + stepsCount++; + + if (DbMode.USE_LOCAL.equals(dbMode)) { + // use local data source (requires migrate) + stepsCount += 2; + } + + // feed back on loading + stepsCount++; + + int openIds = config.getNavigationEditModelCount(); + if (openIds > 0) { + stepsCount += 1 + openIds; + } + int selectedIds = config.getNavigationSelectModelCount(); + if (selectedIds > 0) { + stepsCount += 1 + selectedIds; + } + stepsCount += floatingObjectPresetsManager.size(); + return stepsCount; + } + +} ===================================== client-datasource-editor-api/src/main/java/fr/ird/observe/client/datasource/editor/wizard/StorageUIHandler.java ===================================== @@ -299,305 +299,11 @@ public class StorageUIHandler implements UIHandler<StorageUI>, WithDecoratorServ } } - /** - * Utiliser le storage defini dans le modèle donné. - * - * @param model le model du storage a creer ou utiliser - */ - public void doChangeStorage(StorageUIModel model) { - - ClientConfig config = getClientConfig(); - - // faut-il detruire la base locale ? - boolean destroyLocalBase = config.isLocalStorageExist() && model.getDbMode() == DbMode.CREATE_LOCAL; - - log.debug(">>> should destroy local db ? " + destroyLocalBase); - - ObserveDataSourcesManager dataSourcesManager = getObserveDataSourcesManager(); - ObserveSwingDataSource currentDataSource = dataSourcesManager.getMainDataSource(); - - ObserveSwingDataSource localDataSource = null; - - if (currentDataSource != null && currentDataSource.isLocal()) { - localDataSource = currentDataSource; - } - - ProgressModel progressModel = model.getProgressModel(); - - int stepsCount = 8; - - if (destroyLocalBase || model.isDoBackup()) { - stepsCount++; - } - if (destroyLocalBase) { - stepsCount++; - } - if (model.isDoBackup()) { - stepsCount++; - } - if (currentDataSource != null && currentDataSource.isOpen()) { - stepsCount++; - } - if (DbMode.USE_LOCAL.equals((model.getDbMode()))) { - stepsCount += 2; - } - int openIds = config.getNavigationEditModelCount(); - if (openIds > 0) { - stepsCount += 1 + openIds; - } - int selectedIds = config.getNavigationSelectModelCount(); - if (selectedIds > 0) { - stepsCount += 1 + selectedIds; - } - stepsCount += getFloatingObjectPresetsManager().size(); - - progressModel.installUI(ui.getCONFIRM().getProgressBar()); - progressModel.setMaximum(stepsCount); - log.info(String.format("Found %d steps to load new data source.", stepsCount)); - - boolean localDbIsSane = true; - if (destroyLocalBase || model.isDoBackup()) { - if (localDataSource == null) { - - ObserveDataSourceConfigurationTopiaH2 localConfiguration = dataSourcesManager.newH2DataSourceConfiguration(t("observe.runner.initStorage.label.local")); - - // la base ne doit pas etre mise a jour dans ce cas - localConfiguration.setCanMigrate(false); - - // on charge un storage sur la base locale - localDataSource = dataSourcesManager.newDataSource(localConfiguration); - } - - Objects.requireNonNull(localDataSource); - - // Let's check if the datasource is opened or not : - // we could have close the datasource through the ui - // If the datasource is closed, we try to open it - if (!localDataSource.isOpen()) { - try { - - localDataSource.open(); - - } catch (Exception e) { - // on a pas reussi à ouvrir la base locale - // cela ne doit pas empécher de continuer - // il faut juste supprimer physiquement le repertoire - // de la base - log.error(t("observe.error.storage.could.not.load.local.db", e.getMessage()), e); - - // on conserve l'état - localDbIsSane = false; - - // pour la suite on fait comme si il n'y a pas de local storage - localDataSource = null; - } - } - - progressModel.setExtent(1); - } - - if (model.isDoBackup()) { - if (!localDbIsSane) { - - // la base locale n'est pas saine, on doit arrêter l'operation - // de changement de base sous peine de perdre la base. - Exception e = new Exception(t("observe.error.storage.could.not.backup.unsane.local.db")); - UIHelper.handlingError(e); - return; - } - - Objects.requireNonNull(localDataSource); - - // effectue la backup de la base locale existante - File f = model.getBackupFile(); - log.debug(">>> do backup with " + localDataSource + " in " + f); - try { - SqlScriptProducerService dumpProducerService = localDataSource.getSqlScriptProducerService(); - backupLocalDatabase(dumpProducerService, f); - } catch (Exception e) { - UIHelper.handlingError(e); - return; - } - - progressModel.setExtent(1); - } - - if (destroyLocalBase) { - log.debug(">>> destroy local db " + localDataSource); - if (!localDbIsSane) { - // la base locale n'est pas saine, on va supprimer directement - // le dossier de la base - File localDBDirectory = config.getLocalDBDirectory(); - log.info(">>> destroy local db directory " + localDBDirectory); - try { - FileUtils.deleteDirectory(localDBDirectory); - } catch (IOException e) { - UIHelper.handlingError(e); - } - } else { - try { - localDataSource.destroy(); - } catch (Exception e) { - UIHelper.handlingError(e); - return; - } - } - - progressModel.setExtent(1); - } - - // suppression du storage precedent - if (currentDataSource != null && currentDataSource.isOpen()) { - log.debug(">>> close main storage " + currentDataSource); - // on doit fermer le storage en cours d'utilisation - try { - currentDataSource.close(); - } catch (Exception e) { - UIHelper.handlingError(e); - } - } - progressModel.setExtent(1); - - // suppression du storage local - if (localDataSource != null - && localDataSource.isOpen() // Si la base a été détruite précédemment, elle n'est plus ouverte - && localDataSource != currentDataSource) { - // ce cas peut arriver lorsque l'on fait juste une backup - // sans vouloir supprimer la base locale - log.debug(">>> close local storage " + localDataSource); - // on doit fermer le storage local ouvert - try { - localDataSource.close(); - } catch (Exception e) { - UIHelper.handlingError(e); - } - } - - log.debug("Will create new storage..."); - - // preparation du nouveau storage - - try { - currentDataSource = model.initFromDataSource(config, dataSourcesManager); - currentDataSource.setProgressModel(progressModel); - - // si on utilise la base local on lance une migration de la base si necessaire - if (DbMode.USE_LOCAL.equals((model.getDbMode()))) { - - ObserveDataSourceInformation dataSourceInformation = currentDataSource.checkCanConnect(false); - currentDataSource.setOwner(dataSourceInformation.isOwner()); - currentDataSource.setSuperUser(dataSourceInformation.isSuperUser()); - progressModel.setExtent(1); - - currentDataSource.migrateData(dataSourceInformation, config.getModelVersion()); - - progressModel.setExtent(1); - - } - - //FIXME:BodyContent this is already done in prepareMainStorage method -// dataSourcesManager.setMainDataSource(currentDataSource); - ObserveMainUI mainUI = getClientUIContext().getMainUI(); - - mainUI.getMainUIBodyContentManager().getBodyTyped(DataSourceEditor.class, DataSourceEditorBodyContent.class).prepareMainStorage(currentDataSource, true); - - if (model.getDbMode() == DbMode.CREATE_LOCAL) { - - DataSourceCreateConfigurationDto creationConfigurationDto = model.getCreationConfigurationDto(); - - try { - - currentDataSource.create(creationConfigurationDto); - - } catch (Exception e) { - //si il y a une erreur lor de la création de la base locla - - // on suprimer la base - File localDBDirectory = config.getLocalDBDirectory(); - log.info(">>> destroy local db directory " + localDBDirectory); - try { - FileUtils.deleteDirectory(localDBDirectory); - } catch (IOException e2) { - UIHelper.handlingError(e2); - } - - config.setLocalStorageExist(false); - dataSourcesManager.setMainDataSource(null); - - throw e; - } - } else { - // ouverture du nouveau storage - currentDataSource.open(); - } - - progressModel.setExtent(1); - - if (DbMode.CREATE_LOCAL.equals(model.getDbMode()) - && (IMPORT_REMOTE_STORAGE.equals(model.getCreationMode()) || IMPORT_SERVER_STORAGE.equals(model.getCreationMode())) - && config.isLocalStorageExist()) { - // si on a creer la base locale a partir d'un import d'une base - // distante, on sauvegarde la base locale comme dump initial - // il s'agit d'un dump du référentiel - File f = config.getInitialDbDump(); - if (f.exists()) { - // on supprime le dump sql de la base embarquée - if (!f.delete()) { - throw new IllegalStateException("could not delete " + f); - } - } - log.info(">>> create initial dump with " + currentDataSource + " in " + f); - try { - SqlScriptProducerService dumpProducerService = currentDataSource.getSqlScriptProducerService(); - backupLocalDatabase(dumpProducerService, f); - config.setInitialDumpExist(true); - } catch (Exception e) { - UIHelper.handlingError(e); - } - } - - log.info(">>> main storage opened " + currentDataSource.getLabel()); - - progressModel.setExtent(1); - - mainUI.changeBodyContent(DataSourceEditor.class); - - } catch (Exception ex) { - UIHelper.handlingError(ex); - throw new RuntimeException(ex); - } finally { - ObserveUtil.cleanMemory(); - } - } - - /** - * Effectue une sauvegarde de la base locale vers le fichier choisi. - * - * @param dumpProducerService le service de dump - * @param dst le fichier de sauvegarde - */ - private void backupLocalDatabase(SqlScriptProducerService dumpProducerService, File dst) { - if (dst == null) { - throw new IllegalArgumentException("file where to backup can not be null"); - } - if (log.isDebugEnabled()) { - log.debug(dst); - } - - AddSqlScriptProducerRequest request = AddSqlScriptProducerRequest.forH2(getClientConfig().getModelVersion()).addSchema().addReferential().addAllData(); - TopiaSqlScript dataDump = dumpProducerService.produceAddSqlScript(request); - try { - dataDump.copy(dst.toPath()); - } catch (IOException e) { - UIHelper.handlingError(e); - } - } - public void destroy() { ui.getModel().destroy(); log.debug("destroy ui " + ui.getName()); UIHelper.TabbedPaneIterator<Component> itr = UIHelper.newTabbedPaneIterator(ui.getTabs()); - for (; itr.hasNext(); ) { + while (itr.hasNext()) { StorageTabUI tab = (StorageTabUI) itr.next(); log.debug("destroy ui " + tab.getName()); tab.destroy(); ===================================== client-datasource-editor-api/src/main/java/fr/ird/observe/client/datasource/editor/wizard/StorageUILauncher.java ===================================== @@ -25,12 +25,9 @@ import fr.ird.observe.client.ClientUIContextApplicationComponent; import fr.ird.observe.client.WithClientUIContext; import fr.ird.observe.client.configuration.WithClientConfig; import fr.ird.observe.client.constants.DbMode; -import fr.ird.observe.client.datasource.api.ObserveDataSourcesManagerApplicationComponent; -import fr.ird.observe.client.datasource.api.ObserveSwingDataSource; import fr.ird.observe.client.datasource.api.WithObserveDataSourcesManager; import fr.ird.observe.client.util.UIHelper; import fr.ird.observe.dto.ObserveUtil; -import org.apache.commons.collections4.CollectionUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.nuiton.jaxx.runtime.JAXXContext; @@ -41,8 +38,6 @@ import org.nuiton.jaxx.runtime.swing.wizard.WizardUILancher; import java.awt.Window; import java.util.Arrays; -import java.util.Set; -import java.util.stream.Collectors; import static io.ultreia.java4all.i18n.I18n.t; @@ -68,63 +63,6 @@ public class StorageUILauncher extends WizardUILancher<StorageStep, StorageUIMod this.title = title; } - /** - * Méthode pour lancer le changement de source de données. - * - * @param rootContext le context applicatif - * @param mainUI main ui - * @param modes les modes optionnel à utiliser - * @param title le titre de la fenetre - * @see StorageUIHandler - * @see StorageUI - */ - public static void changeStorage(JAXXContext rootContext, Window mainUI, Set<DbMode> modes, String title) { - - new StorageUILauncher(rootContext, mainUI, title) { - - @Override - protected void init(StorageUI ui) { - super.init(ui); - log.debug("Incoming db mode : " + modes.stream().map(DbMode::name).collect(Collectors.joining(", "))); - StorageUIModel model = ui.getModel(); - if (CollectionUtils.isNotEmpty(modes)) { - log.info("will use incoming mode " + modes.stream().map(DbMode::name).collect(Collectors.joining(", "))); - - model.setExcludeSteps(Arrays.asList(StorageStep.SELECT_DATA, - StorageStep.BACKUP, - StorageStep.CONFIG_REFERENTIAL, - StorageStep.CONFIG_DATA, - StorageStep.ROLES)); - model.setCanCreateLocalService(modes.contains(DbMode.CREATE_LOCAL)); - model.setCanUseLocalService(modes.contains(DbMode.USE_LOCAL)); - model.setCanUseRemoteService(modes.contains(DbMode.USE_REMOTE)); - model.setCanUseServerService(modes.contains(DbMode.USE_SERVER)); - - model.updateUniverse(); - model.setDbMode(modes.stream().findFirst().orElse(null)); - - } else { - boolean localOpened = ObserveDataSourcesManagerApplicationComponent.value().getOptionalMainDataSource().map(ObserveSwingDataSource::isLocal).orElse(false); - if (localOpened) { - log.debug("Can not use local db (already opened)"); - model.setCanUseLocalService(false); - } - model.setCanCreateLocalService(true); - model.setCanUseRemoteService(true); - model.setCanUseServerService(true); - } - model.updateUniverse(); - } - - @Override - public void doAction(StorageUI ui) { - super.doAction(ui); - ui.getHandler().doChangeStorage(ui.getModel()); - } - - }.start(); - } - /** * Méthode pour lancer l'action de connexion à une base distante. * <p> ===================================== client-datasource-editor-api/src/main/java/fr/ird/observe/client/datasource/editor/wizard/StorageUIModel.java ===================================== @@ -79,6 +79,7 @@ import java.io.File; import java.lang.reflect.UndeclaredThrowableException; import java.net.MalformedURLException; import java.net.URL; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -444,27 +445,56 @@ public class StorageUIModel extends WizardModel<StorageStep> implements WithClie setDataSourceInformation(previousInfo); } - public ObserveSwingDataSource initFromDataSource(ClientConfig config, ObserveDataSourcesManager dataSourcesManager) { + public ObserveSwingDataSource initDataSourceFromModel(ClientConfig config, ObserveDataSourcesManager dataSourcesManager) { ObserveSwingDataSource dataSource = null; ObserveDataSourceConfiguration configuration; switch (getDbMode()) { case CREATE_LOCAL: - case USE_LOCAL: configuration = toH2StorageConfig(t("observe.runner.initStorage.label.local")); dataSource = dataSourcesManager.newDataSource(configuration); - dataSource.addObserveSwingDataSourceListener( new ObserveSwingDataSourceListenerAdapter() { @Override public void onOpened(ObserveSwingDataSourceEvent event) { - // la base locale existe desormais + ObserveSwingDataSource dataSource = event.getSource(); + + // local data source now exists config.setLocalStorageExist(true); + + switch (getCreationMode()) { + case IMPORT_EXTERNAL_DUMP: + // update import directory + File importDirectory = getDumpFile().getParentFile(); + log.info(String.format("update import directory to: %s", importDirectory)); + config.setImportDirectory(importDirectory); + break; + case IMPORT_REMOTE_STORAGE: + case IMPORT_SERVER_STORAGE: + // Let's generate again initial dump + Path f = config.getInitialDbDump().toPath(); + log.info(String.format("create initial dump with %s to: %s", dataSource, f)); + try { + dataSource.backupLocalDatabase(f); + config.setInitialDumpExist(true); + } catch (Exception e) { + UIHelper.handlingError(e); + //FIXME FeedBack ??? + + } + break; + } + config.saveForUser(); + } }); + break; + case USE_LOCAL: + configuration = toH2StorageConfig(t("observe.runner.initStorage.label.local")); + dataSource = dataSourcesManager.newDataSource(configuration); break; case USE_REMOTE: configuration = toPGStorageConfig(t("observe.storage.label.remote")); ===================================== client-datasource-editor-api/src/main/java/fr/ird/observe/client/datasource/editor/wizard/connexion/DataSourceSelectorModel.java ===================================== @@ -154,7 +154,7 @@ public class DataSourceSelectorModel extends StorageUIModel { public ObserveSwingDataSource getSafeSource(boolean open) { if (source == null || open && !source.isOpen()) { - source = initFromDataSource(getClientConfig(), ObserveDataSourcesManagerApplicationComponent.value()); + source = initDataSourceFromModel(getClientConfig(), ObserveDataSourcesManagerApplicationComponent.value()); } if (open) { ===================================== client-runner/src/main/java/fr/ird/observe/client/RunObserve.java ===================================== @@ -360,6 +360,10 @@ public class RunObserve extends ApplicationRunner implements WithBackupsManager, } } + //FIXME Reuse the load storage action which will soon have the logic to load back a backup and anything else + //FIXME Need to have a unified API and only one + //FIXME first try to open specified ds (here local ds) + //FIXME if can not load local ds, then feedback it and propose to load automatic backup, or to create a new local ds private void initStorage(ClientConfig config, JFrame ui) { ObserveMainUI mainUI = (ObserveMainUI) ui; @@ -415,10 +419,10 @@ public class RunObserve extends ApplicationRunner implements WithBackupsManager, } - private void askToCreateLocalDatabase(ObserveMainUI mainUI, - ClientConfig config, - ObserveDataSourcesManager dataSourcesManager, - BackupStorage lastAutomaticBackup) { + public static void askToCreateLocalDatabase(ObserveMainUI mainUI, + ClientConfig config, + ObserveDataSourcesManager dataSourcesManager, + BackupStorage lastAutomaticBackup) { InitStorageModel initStorageModel = new InitStorageModel(config.getLocalDBDirectory(), lastAutomaticBackup); @@ -444,8 +448,7 @@ public class RunObserve extends ApplicationRunner implements WithBackupsManager, } - JLabel label = new JLabel(); - label.setText(text); + JLabel label = new JLabel(text); int response = askUser( null, t("observe.runner.initStorage.title.no.local.db.found"), @@ -454,7 +457,7 @@ public class RunObserve extends ApplicationRunner implements WithBackupsManager, options, defaultOption ); - log.debug("response : " + response); + log.debug(String.format("response : %d", response)); if (noAutomaticBackup) { Set<DbMode> dbModes = EnumSet.noneOf(DbMode.class); @@ -510,7 +513,7 @@ public class RunObserve extends ApplicationRunner implements WithBackupsManager, } - private void loadBackup(ObserveDataSourcesManager dataSourcesManager, ObserveMainUI mainUI, BackupStorage backupStorage) { + public static void loadBackup(ObserveDataSourcesManager dataSourcesManager, ObserveMainUI mainUI, BackupStorage backupStorage) { log.info("Will load last backup: " + backupStorage.getFile()); try { // byte[] dump = Files.readAllBytes(backupStorage.getFile().toPath()); ===================================== observe-i18n/src/main/i18n/translations/observe_en_GB.properties ===================================== @@ -388,6 +388,7 @@ observe.choice.createLocalStorage=Create local storage observe.choice.dcp.default=Other opérations or objects (FOB) observe.choice.doNotSave=Do not save observe.choice.doNothing=Do nothing +observe.choice.generateFeedBack=Generate observe.choice.loadLastAutomaticBackup=Use last automatic backup observe.choice.quit=Quit observe.choice.replace=Replace @@ -479,6 +480,7 @@ observe.config.defaultReportDirectory.description=Default directory where to sto observe.config.defaultResourcesDirectory.description=Default user resources directory observe.config.defaultValidationReportDirectory.description=Default validation report directory observe.config.devMode=Dev mode +observe.config.feedBackDirectory.description=FeedBack directory observe.config.floatingObjectPresets.description=Floating Objects references observe.config.h2.can.editReferential.description=Local database can edit referential observe.config.h2.can.migrate.description=Flag to know if you can migrate h2 data sources @@ -2299,7 +2301,10 @@ observe.error.LengthLengthParameterNotFoundException=No length length relation f observe.error.LengthWeightParameterNotFoundException=No length weight relation found for species\: %s, ocean\: %s, sex\: %s, date\: %s \: %s observe.error.can.not.create.directory=Can't create directory %&$s\! observe.error.storage.could.not.backup.unsane.local.db=Can not save an borken database +observe.error.storage.could.not.close.local.data.source.message=A feed back backup can be generate at %s, otherwise you can cancel. +observe.error.storage.could.not.close.local.data.source.title=An error occurs while removing previous local data source observe.error.storage.could.not.load.local.db=Could not open local database for reason\: %s +observe.error.storage.could.not.open.local.data.source.title=An error occurs while creating local data source observe.info.selected.validators=You must select at least one validator to continue. observe.info.validation.credentials=To validate referentiel (resp. data), You must have rw credentials. observe.init.local.db.detected=%1$s detected. ===================================== observe-i18n/src/main/i18n/translations/observe_es_ES.properties ===================================== @@ -388,6 +388,7 @@ observe.choice.createLocalStorage=Crear la base local observe.choice.dcp.default=Other opérations or objects (FOB) \#TODO observe.choice.doNotSave=No grabar observe.choice.doNothing=No hacer nada +observe.choice.generateFeedBack=Generate \#TODO observe.choice.loadLastAutomaticBackup=Use last automatic backup \#TODO observe.choice.quit=Cerrar observe.choice.replace=Reemplazar @@ -479,6 +480,7 @@ observe.config.defaultReportDirectory.description=Directorio por defecto de los observe.config.defaultResourcesDirectory.description=Directorio de almacenamiento de los recursos de usuario como las traducciones o la consultas de informes observe.config.defaultValidationReportDirectory.description=Directorio por defecto de almacenamiento de los informes de validación observe.config.devMode=Modo desarrollador +observe.config.feedBackDirectory.description=FeedBack directory \#TODO observe.config.floatingObjectPresets.description=Objetos flotantes de referencia observe.config.h2.can.editReferential.description=Para poder editar el referencial de una base local observe.config.h2.can.migrate.description=Autorizar la actualización de las bases locales (h2) @@ -2299,7 +2301,10 @@ observe.error.LengthLengthParameterNotFoundException=No length length relation f observe.error.LengthWeightParameterNotFoundException=No length weight relation found for species\: %s, ocean\: %s, sex\: %s, date\: %s \: %s \#TODO observe.error.can.not.create.directory=¡Imposible crear el directorio %1$s\! observe.error.storage.could.not.backup.unsane.local.db=Impossible grabar una base dañada +observe.error.storage.could.not.close.local.data.source.message=A feed back backup can be generate at %s, otherwise you can cancel. \#TODO +observe.error.storage.could.not.close.local.data.source.title=An error occurs while removing previous local data source \#TODO observe.error.storage.could.not.load.local.db=Impossible grabar la base local por la razón siguiente \: %s +observe.error.storage.could.not.open.local.data.source.title=An error occurs while creating local data source \#TODO observe.info.selected.validators=Es necesario seleccionar un validador para continuar. observe.info.validation.credentials=Para validar el referencial (resp. los datos), debe tener los derechos de lectura y escritura correspondientes. observe.init.local.db.detected=%1$s detectado. ===================================== observe-i18n/src/main/i18n/translations/observe_fr_FR.properties ===================================== @@ -388,6 +388,7 @@ observe.choice.createLocalStorage=Créer la base locale observe.choice.dcp.default=Autre opérations et types d'objets (FOB) observe.choice.doNotSave=Ne pas enregistrer observe.choice.doNothing=Ne rien faire +observe.choice.generateFeedBack=Générer observe.choice.loadLastAutomaticBackup=Charger la dernière sauvegarde automatique observe.choice.quit=Fermer observe.choice.replace=Remplacer @@ -479,6 +480,7 @@ observe.config.defaultReportDirectory.description=Répertoire par défaut des ra observe.config.defaultResourcesDirectory.description=Le répertoire où sont stockées les ressources de l'utilisateur comme les traductions ou les requêtes de rapports. observe.config.defaultValidationReportDirectory.description=Le répertoire par défaut où sont stockés les rapports de validation observe.config.devMode=Mode développeur +observe.config.feedBackDirectory.description=Répertoire où sont consigner les feedback observe.config.floatingObjectPresets.description=Objets flottants de référence observe.config.h2.can.editReferential.description=Pour pouvoir éditer le référentiel d'une base locale observe.config.h2.can.migrate.description=Autoriser la mise à jour des bases locales (H2) @@ -2299,7 +2301,10 @@ observe.error.LengthLengthParameterNotFoundException=Aucune relation taille-tail observe.error.LengthWeightParameterNotFoundException=Aucune relation taille-poids trouvée pour l'espèce\: %s, l'ocean\: %s, le sexe\: %s et la date\: %s observe.error.can.not.create.directory=Création du répertoire %1$s impossible\! observe.error.storage.could.not.backup.unsane.local.db=Impossible d'enregistrer une base non endommagée +observe.error.storage.could.not.close.local.data.source.message=Vous pouvez générer un retour d'expérience utilisateur (avec toutes les données disponibles pour comprendre le problème de puis l'archive %s), ou annuler l'opération de changement de base +observe.error.storage.could.not.close.local.data.source.title=Une erreur est survenue lors de la suppression de l'ancienne base locale observe.error.storage.could.not.load.local.db=Impossible d'ouvrir la base locale pour la raison suivante \: %s +observe.error.storage.could.not.open.local.data.source.title=Une erreur est survenue lors de la création de la nouvelle base locale observe.info.selected.validators=Il faut au moins un validateur sélectionné pour continuer. observe.info.validation.credentials=Pour valider le référentiel (resp. les données), vous devez posséder les droits en lecture correspondants. observe.init.local.db.detected=%1$s détectée. View it on GitLab: https://gitlab.com/ultreiaio/ird-observe/-/commit/3c6f6c90f8ee26b6bf76d7d0b3... -- View it on GitLab: https://gitlab.com/ultreiaio/ird-observe/-/commit/3c6f6c90f8ee26b6bf76d7d0b3... You're receiving this email because of your account on gitlab.com.
participants (1)
-
Tony CHEMIT