From 60b98b36cb819a5c70c438963dcf5ab1354aadbb Mon Sep 17 00:00:00 2001 From: HuDeanY Date: Mon, 4 Mar 2024 00:45:50 +0100 Subject: [PATCH] Improved outputpath handling --- src/LanguageProperties.properties | 4 + src/LanguageProperties_de.properties | 4 + src/de/soderer/dbexport/DbExport.java | 2 +- .../soderer/dbexport/DbExportDefinition.java | 32 ++++++ src/de/soderer/dbexport/DbExportGui.java | 21 +++- .../worker/AbstractDbExportWorker.java | 97 ++++++++++++++++--- .../dbexport/worker/DbKdbxExportWorker.java | 1 - src/help.txt | 2 + 8 files changed, 146 insertions(+), 17 deletions(-) diff --git a/src/LanguageProperties.properties b/src/LanguageProperties.properties index 5f90baa..8940071 100644 --- a/src/LanguageProperties.properties +++ b/src/LanguageProperties.properties @@ -126,3 +126,7 @@ sureQuestion=Are you sure? exportSpeed=Speed error.userNotAuthenticatedOrNotAuthorized=User not authenticated or not authorized None=None +createOutputDirectoyIfNotExists=Create output directory if it is missing +createOutputDirectoyIfNotExists_help=Create output directory if it is missing +replaceAlreadyExistingFiles=Replace existing previously created export files +replaceAlreadyExistingFiles_help=Replace existing previously created export files diff --git a/src/LanguageProperties_de.properties b/src/LanguageProperties_de.properties index 3e27298..9b035c4 100644 --- a/src/LanguageProperties_de.properties +++ b/src/LanguageProperties_de.properties @@ -126,3 +126,7 @@ sureQuestion=Sind sie sich sicher? exportSpeed=Geschwindigkeit error.userNotAuthenticatedOrNotAuthorized=Benutzerdaten sind nicht gültig oder Benutzer ist nicht berechtigt None=Keine +createOutputDirectoyIfNotExists=Ausgabeverzeichnis anlegen wenn nicht vorhanden +createOutputDirectoyIfNotExists_help=Ausgabeverzeichnis anlegen wenn nicht vorhanden +replaceAlreadyExistingFiles=Bestehende Dateien ersetzen +replaceAlreadyExistingFiles_help=Bestehende Dateien ersetzen diff --git a/src/de/soderer/dbexport/DbExport.java b/src/de/soderer/dbexport/DbExport.java index f032f6c..aa17bc8 100644 --- a/src/de/soderer/dbexport/DbExport.java +++ b/src/de/soderer/dbexport/DbExport.java @@ -45,6 +45,7 @@ import de.soderer.utilities.http.HttpUtilities; import de.soderer.utilities.worker.WorkerParentDual; +// TODO: Export/Import KDBX entry path /** * The Main-Class of DbExport. */ @@ -748,7 +749,6 @@ private static int connectionTest(final ConnectionTestDefinition connectionTestD for (int i = 1; i <= connectionTestDefinition.getIterations() || connectionTestDefinition.getIterations() == 0; i++) { connectionCheckCount++; System.out.println("Connection test " + i + (connectionTestDefinition.getIterations() > 0 ? " / " + connectionTestDefinition.getIterations() : "")); - @SuppressWarnings("resource") Connection testConnection = null; try { System.out.println(DateUtilities.formatDate(DateUtilities.YYYY_MM_DD_HHMMSS, LocalDateTime.now()) + ": Creating database connection"); diff --git a/src/de/soderer/dbexport/DbExportDefinition.java b/src/de/soderer/dbexport/DbExportDefinition.java index 41f6d17..cccf566 100644 --- a/src/de/soderer/dbexport/DbExportDefinition.java +++ b/src/de/soderer/dbexport/DbExportDefinition.java @@ -133,6 +133,10 @@ public static DataType getFromString(final String dataTypeString) { /** The null value string. */ private String nullValueString = ""; + private boolean replaceAlreadyExistingFiles = false; + + private boolean createOutputDirectoyIfNotExists = false; + /** * Sets the data type. * @@ -712,6 +716,22 @@ public boolean isExportStructure() { return exportStructure; } + public boolean isReplaceAlreadyExistingFiles() { + return replaceAlreadyExistingFiles; + } + + public void setReplaceAlreadyExistingFiles(final boolean replaceAlreadyExistingFiles) { + this.replaceAlreadyExistingFiles = replaceAlreadyExistingFiles; + } + + public boolean isCreateOutputDirectoyIfNotExists() { + return createOutputDirectoyIfNotExists; + } + + public void setCreateOutputDirectoyIfNotExists(final boolean createOutputDirectoyIfNotExists) { + this.createOutputDirectoyIfNotExists = createOutputDirectoyIfNotExists; + } + /** * Create and configure a worker according to the current configuration * @@ -819,6 +839,8 @@ public AbstractDbExportWorker getConfiguredWorker(final WorkerParentDual parent) worker.setExportStructure(isExportStructure()); worker.setDatabaseTimeZone(getDatabaseTimeZone()); worker.setExportDataTimeZone(getExportDataTimeZone()); + worker.setReplaceAlreadyExistingFiles(isReplaceAlreadyExistingFiles()); + worker.setCreateOutputDirectoyIfNotExists(isCreateOutputDirectoyIfNotExists()); return worker; } @@ -916,6 +938,12 @@ public String toParamsString() { if (!"".equals(getNullValueString())) { params += " " + "-n" + " '" + getNullValueString() + "'"; } + if (isCreateOutputDirectoyIfNotExists()) { + params += " " + "-createOutputDirectoyIfNotExists"; + } + if (isReplaceAlreadyExistingFiles()) { + params += " " + "-replaceAlreadyExistingFiles"; + } return params; } @@ -952,6 +980,8 @@ public void importParameters(final DbDefinition otherDbDefinition) { noHeaders = false; exportStructure = false; nullValueString = ""; + createOutputDirectoyIfNotExists = false; + replaceAlreadyExistingFiles = false; } else if (otherDbDefinition instanceof DbExportDefinition) { final DbExportDefinition otherDbExportDefinition = (DbExportDefinition) otherDbDefinition; sqlStatementOrTablelist = otherDbExportDefinition.getSqlStatementOrTablelist(); @@ -986,6 +1016,8 @@ public void importParameters(final DbDefinition otherDbDefinition) { noHeaders = otherDbExportDefinition.isNoHeaders(); exportStructure = otherDbExportDefinition.isExportStructure(); nullValueString = otherDbExportDefinition.getNullValueString(); + createOutputDirectoyIfNotExists = otherDbExportDefinition.isCreateOutputDirectoyIfNotExists(); + replaceAlreadyExistingFiles = otherDbExportDefinition.isReplaceAlreadyExistingFiles(); } } } diff --git a/src/de/soderer/dbexport/DbExportGui.java b/src/de/soderer/dbexport/DbExportGui.java index 1c0acf7..78aaa3e 100644 --- a/src/de/soderer/dbexport/DbExportGui.java +++ b/src/de/soderer/dbexport/DbExportGui.java @@ -169,6 +169,10 @@ public class DbExportGui extends UpdateableGuiApplication { /** The no headers box. */ private final JCheckBox noHeadersBox; + private final JCheckBox createOutputDirectoyIfNotExistsBox; + + private final JCheckBox replaceAlreadyExistingFilesBox; + /** The temporary preferences password. */ private char[] temporaryPreferencesPassword = null; @@ -760,6 +764,14 @@ public void itemStateChanged(final ItemEvent event) { exportStructureBox.setToolTipText(LangResources.get("exportstructure_help")); optionalParametersPanel.add(exportStructureBox); + createOutputDirectoyIfNotExistsBox = new JCheckBox(LangResources.get("createOutputDirectoyIfNotExists")); + createOutputDirectoyIfNotExistsBox.setToolTipText(LangResources.get("createOutputDirectoyIfNotExists_help")); + optionalParametersPanel.add(createOutputDirectoyIfNotExistsBox); + + replaceAlreadyExistingFilesBox = new JCheckBox(LangResources.get("replaceAlreadyExistingFiles")); + replaceAlreadyExistingFilesBox.setToolTipText(LangResources.get("replaceAlreadyExistingFiles_help")); + optionalParametersPanel.add(replaceAlreadyExistingFilesBox); + // Button Panel final JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS)); @@ -941,6 +953,9 @@ private DbExportDefinition getConfigurationAsDefinition() throws Exception { dbExportDefinition.setDatabaseTimeZone((String) databaseTimezoneCombo.getSelectedItem()); dbExportDefinition.setExportDataTimeZone((String) exportDataTimezoneCombo.getSelectedItem()); + dbExportDefinition.setCreateOutputDirectoyIfNotExists(createOutputDirectoyIfNotExistsBox.isSelected()); + dbExportDefinition.setReplaceAlreadyExistingFiles(replaceAlreadyExistingFilesBox.isSelected()); + return dbExportDefinition; } @@ -998,6 +1013,8 @@ private void setConfigurationByDefinition(final DbExportDefinition dbExportDefin beautifyBox.setSelected(dbExportDefinition.isBeautify()); exportStructureBox.setSelected(dbExportDefinition.isExportStructure()); noHeadersBox.setSelected(dbExportDefinition.isNoHeaders()); + createOutputDirectoyIfNotExistsBox.setSelected(dbExportDefinition.isCreateOutputDirectoyIfNotExists()); + replaceAlreadyExistingFilesBox.setSelected(dbExportDefinition.isReplaceAlreadyExistingFiles()); boolean encodingFound = false; for (int i = 0; i < encodingCombo.getItemCount(); i++) { @@ -1231,9 +1248,7 @@ private void export(final DbExportDefinition dbExportDefinition, final DbExportG final AbstractDbExportWorker worker = dbExportDefinition.getConfiguredWorker(null); final Result result; - if (dbExportDefinition.getSqlStatementOrTablelist().toLowerCase().startsWith("select ") || - (!dbExportDefinition.getSqlStatementOrTablelist().contains("*") - && !dbExportDefinition.getSqlStatementOrTablelist().contains("?"))) { + if (worker.isSingleExport()) { final ProgressDialog progressDialog = new ProgressDialog<>(dbExportGui, DbExport.APPLICATION_NAME, null, worker); result = progressDialog.open(); } else { diff --git a/src/de/soderer/dbexport/worker/AbstractDbExportWorker.java b/src/de/soderer/dbexport/worker/AbstractDbExportWorker.java index 3503a0b..0af0978 100644 --- a/src/de/soderer/dbexport/worker/AbstractDbExportWorker.java +++ b/src/de/soderer/dbexport/worker/AbstractDbExportWorker.java @@ -89,6 +89,8 @@ public abstract class AbstractDbExportWorker extends WorkerDual { protected Character decimalSeparator; protected boolean beautify = false; protected boolean exportStructure = false; + protected boolean replaceAlreadyExistingFiles = false; + protected boolean createOutputDirectoyIfNotExists = false; private int overallExportedLines = 0; private long overallExportedDataAmountRaw = 0; @@ -169,6 +171,14 @@ public void setExportStructure(final boolean exportStructure) { this.exportStructure = exportStructure; } + public void setCreateOutputDirectoyIfNotExists(final boolean createOutputDirectoyIfNotExists) { + this.createOutputDirectoyIfNotExists = createOutputDirectoyIfNotExists; + } + + public void setReplaceAlreadyExistingFiles(final boolean replaceAlreadyExistingFiles) { + this.replaceAlreadyExistingFiles = replaceAlreadyExistingFiles; + } + public void setDateFormat(final String dateFormat) { if (dateFormat != null) { dateFormatPattern = dateFormat; @@ -221,6 +231,32 @@ protected DateTimeFormatter getDateTimeFormatter() { return dateTimeFormatterCache; } + public boolean isSingleExport() { + if (sqlStatementOrTablelist.toLowerCase().startsWith("select ") + || sqlStatementOrTablelist.toLowerCase().startsWith("select\t") + || sqlStatementOrTablelist.toLowerCase().startsWith("select\n") + || sqlStatementOrTablelist.toLowerCase().startsWith("select\r")) { + return true; + } else { + boolean isFirstTableName = true; + for (final String tablePattern : sqlStatementOrTablelist.split(",| |;|\\||\n")) { + if (Utilities.isNotBlank(tablePattern)) { + if (tablePattern.contains("*") || tablePattern.contains("?")) { + return false; + } + if (!tablePattern.startsWith("!")) { + if (!isFirstTableName) { + return false; + } + isFirstTableName = false; + } + } + } + return true; + } + + } + @Override public Boolean work() throws Exception { overallExportedLines = 0; @@ -268,6 +304,7 @@ public Boolean work() throws Exception { throw new DbExportException("Statementfile is missing"); } else { sqlStatementOrTablelist = Utilities.replaceUsersHome(sqlStatementOrTablelist); + sqlStatementOrTablelist = DateUtilities.replaceDatePatternInString(sqlStatementOrTablelist, LocalDateTime.now()); if (!new File(sqlStatementOrTablelist).exists()) { throw new DbExportException("Statementfile does not exist"); } else { @@ -280,20 +317,14 @@ public Boolean work() throws Exception { throw new DbExportException("SqlStatement or tablelist is missing"); } + outputpath = DateUtilities.replaceDatePatternInString(outputpath, LocalDateTime.now()); + if (sqlStatementOrTablelist.toLowerCase().startsWith("select ") || sqlStatementOrTablelist.toLowerCase().startsWith("select\t") || sqlStatementOrTablelist.toLowerCase().startsWith("select\n") || sqlStatementOrTablelist.toLowerCase().startsWith("select\r")) { + // Single export if (!"console".equalsIgnoreCase(outputpath) && !"gui".equalsIgnoreCase(outputpath)) { - if (!new File(outputpath).exists()) { - final int lastSeparator = Math.max(outputpath.lastIndexOf("/"), outputpath.lastIndexOf("\\")); - if (lastSeparator >= 0) { - String filename = outputpath.substring(lastSeparator + 1); - filename = DateUtilities.replaceDatePatternInString(filename, LocalDateTime.now()); - outputpath = outputpath.substring(0, lastSeparator + 1) + filename; - } - } - if (new File(outputpath).exists() && new File(outputpath).isDirectory()) { outputpath = outputpath + File.separator + "export_" + DateUtilities.formatDate("yyyy-MM-dd_HH-mm-ss", LocalDateTime.now()); } @@ -315,7 +346,23 @@ public Boolean work() throws Exception { } if (tablesToExport.size() == 0) { throw new DbExportException("No table found for export"); + } else if (tablesToExport.size() > 1) { + // Multi export + final String basicOutputFilePath = outputpath; + // Create directory if missing + final File outputBaseDirecory = new File(basicOutputFilePath); + if (!outputBaseDirecory.exists()) { + if (createOutputDirectoyIfNotExists) { + outputBaseDirecory.mkdirs(); + } else { + throw new DbExportException("Outputpath '" + basicOutputFilePath + "' does not exist"); + } + } else if (!outputBaseDirecory.isDirectory()) { + throw new DbExportException("Outputpath '" + basicOutputFilePath + "' already exists but is not a directory, which is needed for an export of multiple data sets"); + } + outputpath = basicOutputFilePath; } + itemsToDo = tablesToExport.size(); itemsDone = 0; if (exportStructure) { @@ -392,6 +439,22 @@ public Boolean work() throws Exception { } } + private String prepareOutputFilePathForMultiExport(final String basicOutputFilePath) throws DbExportException { + // Create directory if missing + final File outputBaseDirecory = new File(basicOutputFilePath); + if (!outputBaseDirecory.exists()) { + if (createOutputDirectoyIfNotExists) { + outputBaseDirecory.mkdirs(); + } else { + throw new DbExportException("Outputpath '" + basicOutputFilePath + "' does not exist"); + } + } else if (!outputBaseDirecory.isDirectory()) { + throw new DbExportException("Outputpath '" + basicOutputFilePath + "' already exists but is not a directory, which is needed for an export of multiple data sets"); + } + + return basicOutputFilePath; + } + private void exportDbStructure(final Connection connection, final List tablesToExport, String outputFilePath) throws Exception { OutputStream outputStream = null; File tempFile = null; @@ -795,9 +858,19 @@ private void export(final Connection connection, final String sqlStatement, Stri } if (new File(outputFilePath).exists()) { - throw new DbExportException("Outputfile already exists: " + outputFilePath); - } else if (!new File(outputFilePath).getParentFile().exists()) { - throw new DbExportException("Outputfile parent directory does not exist: " + new File(outputFilePath).getParent()); + if (replaceAlreadyExistingFiles) { + new File(outputFilePath).delete(); + } else { + throw new DbExportException("Outputfile already exists: " + outputFilePath); + } + } + + if (!new File(outputFilePath).getParentFile().exists()) { + if (createOutputDirectoyIfNotExists) { + new File(outputFilePath).getParentFile().mkdirs(); + } else { + throw new DbExportException("Outputfile parent directory does not exist: " + new File(outputFilePath).getParent()); + } } if (log) { diff --git a/src/de/soderer/dbexport/worker/DbKdbxExportWorker.java b/src/de/soderer/dbexport/worker/DbKdbxExportWorker.java index 93a9afd..c9c7b50 100644 --- a/src/de/soderer/dbexport/worker/DbKdbxExportWorker.java +++ b/src/de/soderer/dbexport/worker/DbKdbxExportWorker.java @@ -15,7 +15,6 @@ import de.soderer.utilities.kdbx.data.KdbxEntry; import de.soderer.utilities.worker.WorkerParentDual; -//TODO: Export/Import KDBX entry path public class DbKdbxExportWorker extends AbstractDbExportWorker { private char[] kdbxPassword = null; private KdbxWriter kdbxWriter = null; diff --git a/src/help.txt b/src/help.txt index bd35567..71d5768 100644 --- a/src/help.txt +++ b/src/help.txt @@ -52,6 +52,8 @@ Optional parameters for database export -secure: Use TLS/SSL for secure communication with database -truststore '': Filepath to TrustStore in JKS format for encrypted database connections of some database vendors -truststorepassword '': Optional password for TrustStore + -createOutputDirectoyIfNotExists: Create output directory if it is missing + -replaceAlreadyExistingFiles: Replace existing previously created export files Global standalone parameters help: Show this help manual