diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CloudbeaverPlatform.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CloudbeaverPlatform.java index de46bc3cae..a357819177 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CloudbeaverPlatform.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CloudbeaverPlatform.java @@ -49,13 +49,9 @@ import org.jkiss.dbeaver.runtime.qm.QMControllerImpl; import org.jkiss.dbeaver.runtime.qm.QMLogFileWriter; import org.jkiss.dbeaver.utils.ContentUtils; import org.jkiss.dbeaver.utils.GeneralUtils; -import org.jkiss.utils.CommonUtils; -import org.jkiss.utils.StandardConstants; import org.osgi.framework.Bundle; import java.io.File; -import java.io.IOException; -import java.nio.file.Files; import java.util.ArrayList; import java.util.List; @@ -69,7 +65,7 @@ public class CloudbeaverPlatform extends BasePlatformImpl { private static final Log log = Log.getLog(CloudbeaverPlatform.class); - private static final String TEMP_PROJECT_NAME = "cloudbeaver-temp"; + public static final String WORK_DATA_FOLDER_NAME = ".work-data"; static CloudbeaverPlatform instance; @@ -285,30 +281,18 @@ public class CloudbeaverPlatform extends BasePlatformImpl { if (tempFolder == null) { // Make temp folder monitor.subTask("Create temp folder"); - try { - final java.nio.file.Path tempDirectory = Files.createTempDirectory(TEMP_PROJECT_NAME); - tempFolder = tempDirectory.toFile(); - } catch (IOException e) { - final String sysTempFolder = System.getProperty(StandardConstants.ENV_TMP_DIR); - if (!CommonUtils.isEmpty(sysTempFolder)) { - tempFolder = new File(sysTempFolder, TEMP_PROJECT_NAME); - if (!tempFolder.mkdirs()) { - final String sysUserFolder = System.getProperty(StandardConstants.ENV_USER_HOME); - if (!CommonUtils.isEmpty(sysUserFolder)) { - tempFolder = new File(sysUserFolder, TEMP_PROJECT_NAME); - if (!tempFolder.mkdirs()) { - tempFolder = new File(TEMP_PROJECT_NAME); - } - } - - } - } - } + tempFolder = new File(workspace.getAbsolutePath(), WORK_DATA_FOLDER_NAME); } if (!tempFolder.exists() && !tempFolder.mkdirs()) { log.error("Can't create temp directory!"); } - return tempFolder; + File folder = new File(tempFolder, name); + if (folder.exists()) { + if (!folder.mkdirs()) { + log.error("Error creating temp folder '" + folder.getAbsolutePath() + "'"); + } + } + return folder; } @NotNull diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CloudbeaverJettyServer.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CloudbeaverJettyServer.java index e7f0ede36f..0c9aef2350 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CloudbeaverJettyServer.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CloudbeaverJettyServer.java @@ -13,6 +13,7 @@ import org.eclipse.jetty.server.session.FileSessionDataStore; import org.eclipse.jetty.server.session.SessionHandler; import org.eclipse.jetty.servlet.ErrorPageErrorHandler; import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletMapping; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.runtime.DBWorkbench; @@ -39,9 +40,9 @@ public class CloudbeaverJettyServer { ServletContextHandler servletContextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS); servletContextHandler.setResourceBase(application.getContentRoot()); servletContextHandler.setContextPath(application.getRootURI()); - servletContextHandler.addServlet(CloudbeaverStaticServlet.class, application.getRootURI()); - servletContextHandler.addServlet(CloudbeaverImageServlet.class, application.getServicesURI() + "images/*"); - servletContextHandler.addServlet(GraphQLEndpoint.class, application.getServicesURI() + "gql/*"); + servletContextHandler.addServlet(new ServletHolder("static", new CloudbeaverStaticServlet()), application.getRootURI()); + servletContextHandler.addServlet(new ServletHolder("images", new CloudbeaverImageServlet()), application.getServicesURI() + "images/*"); + servletContextHandler.addServlet(new ServletHolder("graphql", new GraphQLEndpoint()), application.getServicesURI() + "gql/*"); servletContextHandler.addEventListener(new CloudbeaverServerContextListener()); // Add extensions from services diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/model/WebConnectionConfig.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/model/WebConnectionConfig.java index 547f2ab860..336ae8833a 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/model/WebConnectionConfig.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/model/WebConnectionConfig.java @@ -18,7 +18,6 @@ package io.cloudbeaver.server.model; import org.jkiss.dbeaver.model.data.json.JSONUtils; import org.jkiss.dbeaver.model.meta.Property; -import org.jkiss.utils.CommonUtils; import java.util.Map; @@ -46,7 +45,7 @@ public class WebConnectionConfig { } public WebConnectionConfig(Map params) { - dataSourceId = CommonUtils.toString(params.get("dataSourceId")); + dataSourceId = JSONUtils.getString(params, "dataSourceId"); driverId = JSONUtils.getString(params, "driverId"); host = JSONUtils.getString(params, "host"); diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/model/session/WebSession.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/model/session/WebSession.java index 7e8143b379..2b53781355 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/model/session/WebSession.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/model/session/WebSession.java @@ -49,6 +49,7 @@ import javax.servlet.http.HttpSession; import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; /** * Web session. @@ -75,6 +76,7 @@ public class WebSession { private final Map asyncTasks = new HashMap<>(); private final Map attributes = new HashMap<>(); + private final Map> attributeDisposers = new HashMap<>(); private DBNModel navigatorModel; private DBNProjectDatabases databases; @@ -318,6 +320,16 @@ public class WebSession { void close() { try { + // Clear attributes + synchronized (attributes) { + for (Map.Entry> attrDisposer : attributeDisposers.entrySet()) { + Object attrValue = attributes.get(attrDisposer.getKey()); + attrDisposer.getValue().apply(attrValue); + } + attributeDisposers.clear(); + attributes.clear(); + } + List conCopy; synchronized (this.connections) { conCopy = new ArrayList<>(this.connections); @@ -522,12 +534,17 @@ public class WebSession { } } - public T getAttribute(String name, DBRCreator creator) { + public T getAttribute(String name, Function creator, Function disposer) { synchronized (attributes) { T value = (T) attributes.get(name); if (value == null) { - value = creator.createObject(null); - attributes.put(name, value); + value = creator.apply(null); + if (value != null) { + attributes.put(name, value); + if (disposer != null) { + attributeDisposers.put(name, (Function) disposer); + } + } } return value; } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/model/sql/WebSQLProcessor.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/model/sql/WebSQLProcessor.java index 924a8123e2..7ab4f3bc82 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/model/sql/WebSQLProcessor.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/model/sql/WebSQLProcessor.java @@ -79,7 +79,7 @@ public class WebSQLProcessor { return connection; } - WebSession getWebSession() { + public WebSession getWebSession() { return webSession; } @@ -328,7 +328,7 @@ public class WebSQLProcessor { } @NotNull - private T getDataContainerByNodePath(DBRProgressMonitor monitor, @NotNull String containerPath, Class type) throws DBException { + public T getDataContainerByNodePath(DBRProgressMonitor monitor, @NotNull String containerPath, Class type) throws DBException { DBNNode node = webSession.getNavigatorModel().getNodeByPath(monitor, containerPath); if (node == null) { throw new DBWebException("Container node '" + containerPath + "' not found"); diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/schema/service.data.transfer.graphqls b/server/bundles/io.cloudbeaver.service.data.transfer/schema/service.data.transfer.graphqls index cde8df2b2c..00aca74f33 100644 --- a/server/bundles/io.cloudbeaver.service.data.transfer/schema/service.data.transfer.graphqls +++ b/server/bundles/io.cloudbeaver.service.data.transfer/schema/service.data.transfer.graphqls @@ -21,7 +21,7 @@ input DataTransferParameters { # - openNewConnection: opens new database connection for data transfer task settings: Object # Processor properties. See DataTransferProcessorInfo.properties - processorProperties: Object + processorProperties: Object! # Data filter settings filter: SQLDataFilter } diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferManager.java b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferManager.java index 08dbfc3742..85333fd9f6 100644 --- a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferManager.java +++ b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferManager.java @@ -16,18 +16,38 @@ */ package io.cloudbeaver.service.data.transfer; -import io.cloudbeaver.api.DBWModel; +import io.cloudbeaver.DBWebException; +import io.cloudbeaver.server.CloudbeaverPlatform; import io.cloudbeaver.server.model.WebAsyncTaskInfo; import io.cloudbeaver.server.model.session.WebSession; +import io.cloudbeaver.server.model.sql.WebSQLContextInfo; +import io.cloudbeaver.server.model.sql.WebSQLProcessor; +import org.jkiss.code.NotNull; +import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.meta.RuntimeAction; +import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor; +import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; +import org.jkiss.dbeaver.model.runtime.DBRRunnableWithResult; +import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor; +import org.jkiss.dbeaver.model.struct.DBSDataContainer; import org.jkiss.dbeaver.model.struct.DBSEntity; +import org.jkiss.dbeaver.tools.transfer.IDataTransferConsumer; +import org.jkiss.dbeaver.tools.transfer.IDataTransferProcessor; +import org.jkiss.dbeaver.tools.transfer.database.DatabaseProducerSettings; +import org.jkiss.dbeaver.tools.transfer.database.DatabaseTransferProducer; import org.jkiss.dbeaver.tools.transfer.registry.DataTransferProcessorDescriptor; import org.jkiss.dbeaver.tools.transfer.registry.DataTransferRegistry; +import org.jkiss.dbeaver.tools.transfer.stream.IStreamDataExporter; +import org.jkiss.dbeaver.tools.transfer.stream.StreamConsumerSettings; import org.jkiss.dbeaver.tools.transfer.stream.StreamTransferConsumer; +import org.jkiss.dbeaver.utils.ContentUtils; import org.jkiss.utils.CommonUtils; -import java.util.Collections; -import java.util.List; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.*; import java.util.stream.Collectors; /** @@ -35,10 +55,21 @@ import java.util.stream.Collectors; */ public class WebDataTransferManager { - private final DBWModel model; + private static final Log log = Log.getLog(WebDataTransferManager.class); - public WebDataTransferManager(DBWModel model) { - this.model = model; + private final File dataExportFolder; + + public WebDataTransferManager() { + dataExportFolder = CloudbeaverPlatform.getInstance().getTempFolder(new VoidProgressMonitor(), "data-transfer"); + + ContentUtils.deleteFileRecursive(dataExportFolder); + if (!dataExportFolder.mkdirs()) { + log.error("Error re-creating temporary folder"); + } + } + + public File getDataExportFolder() { + return dataExportFolder; } @RuntimeAction @@ -53,32 +84,112 @@ public class WebDataTransferManager { @RuntimeAction public WebAsyncTaskInfo dataTransferExportDataFromContainer( - WebSession session, - String connectionId, + WebSQLProcessor sqlProcessor, String containerNodePath, - WebDataTransferParameters parameters) - { + WebDataTransferParameters parameters) throws DBWebException { + DataTransferProcessorDescriptor processor = DataTransferRegistry.getInstance().getProcessor(parameters.getProcessorId()); - return null; + DBSDataContainer dataContainer; + try { + dataContainer = sqlProcessor.getDataContainerByNodePath(sqlProcessor.getWebSession().getProgressMonitor(), containerNodePath, DBSDataContainer.class); + } catch (DBException e) { + throw new DBWebException("Invalid node path: " + containerNodePath, e); + } + + DBRRunnableWithResult runnable = new DBRRunnableWithResult() { + @Override + public void run(DBRProgressMonitor monitor) throws InvocationTargetException { + try { + File exportFile = new File(dataExportFolder, makeUniqueFileName(sqlProcessor, processor)); + try { + exportData(monitor, processor, dataContainer, parameters, exportFile); + } catch (Exception e) { + if (exportFile.exists()) { + if (!exportFile.delete()) { + log.error("Error deleting export file " + exportFile.getAbsolutePath()); + } + } + throw new DBException("Error exporting data", e); + } + WebDataTransferTaskConfig taskConfig = new WebDataTransferTaskConfig(exportFile, parameters); + String exportFileName = CommonUtils.escapeFileName(CommonUtils.truncateString(dataContainer.getName(), 32)); + taskConfig.setExportFileName(exportFileName); + WebDataTransferUtils.getSessionDataTransferConfig(sqlProcessor.getWebSession()).addTask(taskConfig); + + result = exportFile.getName(); + } catch (Throwable e) { + throw new InvocationTargetException(e); + } + } + }; + return sqlProcessor.getWebSession().createAndRunAsyncTask("Data export", runnable); + } + + @NotNull + private String makeUniqueFileName(WebSQLProcessor sqlProcessor, DataTransferProcessorDescriptor processor) { + return sqlProcessor.getWebSession().getId() + "_" + UUID.randomUUID() + "." + WebDataTransferUtils.getProcessorFileExtension(processor); } @RuntimeAction public WebAsyncTaskInfo dataTransferExportDataFromResults( - WebSession session, - String connectionId, - String contextId, + WebSQLContextInfo sqlContextInfo, String resultsId, - WebDataTransferParameters parameters) - { - return null; + WebDataTransferParameters parameters) throws DBWebException { + throw new DBWebException("Not supported"); } @RuntimeAction - public Boolean dataTransferRemoveDataFile(WebSession session, String dataFileId) { + public Boolean dataTransferRemoveDataFile(WebSQLProcessor sqlProcessor, String dataFileId) { return true; } - private WebDataTransferSessionConfig getConfig(WebSession session) { - return session.getAttribute("dataTransfer", x -> new WebDataTransferSessionConfig()); + private void exportData( + DBRProgressMonitor monitor, + DataTransferProcessorDescriptor processor, + DBSDataContainer dataContainer, + WebDataTransferParameters parameters, + File exportFile) throws DBException, IOException + { + IDataTransferProcessor processorInstance = processor.getInstance(); + if (!(processorInstance instanceof IStreamDataExporter)) { + throw new DBException("Invalid processor. " + IStreamDataExporter.class.getSimpleName() + " expected"); + } + IStreamDataExporter exporter = (IStreamDataExporter) processorInstance; + + StreamTransferConsumer consumer = new StreamTransferConsumer(); + StreamConsumerSettings settings = new StreamConsumerSettings(); + + settings.setOutputEncodingBOM(false); + settings.setOpenFolderOnFinish(false); + settings.setOutputFolder(exportFile.getParentFile().getAbsolutePath()); + settings.setOutputFilePattern(exportFile.getName()); + + Map properties = new HashMap<>(); + + Map processorProperties = parameters.getProcessorProperties(); + if (processorProperties == null) processorProperties = Collections.emptyMap(); + for (DBPPropertyDescriptor prop : processor.getProperties()) { + Object propValue = processorProperties.get(CommonUtils.toString(prop.getId())); + properties.put(prop.getId(), propValue != null ? propValue : prop.getDefaultValue()); + } + // Remove extension property (we specify file name directly) + properties.remove(StreamConsumerSettings.PROP_FILE_EXTENSION); + + consumer.initTransfer( + dataContainer, + settings, + new IDataTransferConsumer.TransferParameters(processor.isBinaryFormat(), processor.isHTMLFormat()), + exporter, + properties); + + DatabaseTransferProducer producer = new DatabaseTransferProducer(dataContainer, parameters.getFilter() == null ? null : parameters.getFilter().makeDataFilter()); + DatabaseProducerSettings producerSettings = new DatabaseProducerSettings(); + producerSettings.setExtractType(DatabaseProducerSettings.ExtractType.SINGLE_QUERY); + producerSettings.setQueryRowCount(false); + producerSettings.setOpenNewConnections(CommonUtils.getOption(parameters.getSettings(), "openNewConnection")); + + producer.transferData(monitor, consumer, null, producerSettings, null); + + consumer.finishTransfer(monitor, false); } } diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferParameters.java b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferParameters.java index 8b06a863f5..fb5ae9fde4 100644 --- a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferParameters.java +++ b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferParameters.java @@ -1,6 +1,7 @@ package io.cloudbeaver.service.data.transfer; import io.cloudbeaver.server.model.sql.WebSQLDataFilter; +import org.jkiss.dbeaver.model.data.json.JSONUtils; import java.util.Map; @@ -11,6 +12,16 @@ public class WebDataTransferParameters { private Map processorProperties; private WebSQLDataFilter filter; + public WebDataTransferParameters() { + } + + public WebDataTransferParameters(Map params) { + this.processorId = JSONUtils.getString(params, "processorId"); + this.settings = JSONUtils.getObject(params, "settings"); + this.processorProperties = JSONUtils.getObject(params, "processorProperties"); + this.filter = new WebSQLDataFilter(JSONUtils.getObject(params, "filter")); + } + public String getProcessorId() { return processorId; } diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferServlet.java b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferServlet.java index 8d3630f1a6..09d90dc475 100644 --- a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferServlet.java +++ b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferServlet.java @@ -5,21 +5,30 @@ import io.cloudbeaver.server.CloudbeaverApplication; import io.cloudbeaver.server.CloudbeaverPlatform; import io.cloudbeaver.server.model.session.WebSession; import org.jkiss.dbeaver.Log; +import org.jkiss.dbeaver.tools.transfer.registry.DataTransferProcessorDescriptor; +import org.jkiss.dbeaver.tools.transfer.registry.DataTransferRegistry; +import org.jkiss.utils.CommonUtils; +import org.jkiss.utils.IOUtils; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; public class WebDataTransferServlet extends HttpServlet { private static final Log log = Log.getLog(WebDataTransferServlet.class); private final CloudbeaverApplication application; + private final WebDataTransferManager dtManager; - public WebDataTransferServlet(CloudbeaverApplication application) { + public WebDataTransferServlet(CloudbeaverApplication application, WebDataTransferManager dtManager) { this.application = application; + this.dtManager = dtManager; } @Override @@ -34,8 +43,35 @@ public class WebDataTransferServlet extends HttpServlet { if (webSession == null) { throw new DBWebException("No active session"); } + WebDataTransferSessionConfig dtConfig = WebDataTransferUtils.getSessionDataTransferConfig(webSession); + WebDataTransferTaskConfig taskInfo = dtConfig.getTask(dataFileId); + if (taskInfo == null) { + throw new DBWebException("Session task '" + dataFileId + "' not found"); + } + DataTransferProcessorDescriptor processor = DataTransferRegistry.getInstance().getProcessor(taskInfo.getParameters().getProcessorId()); + if (processor == null) { + throw new DBWebException("Wrong data processor '" + taskInfo.getParameters().getProcessorId() + "'"); + } + String fileName = taskInfo.getExportFileName(); + if (!CommonUtils.isEmpty(fileName)) { + fileName += "." + WebDataTransferUtils.getProcessorFileExtension(processor); + } else { + fileName = taskInfo.getDataFileId(); + } - response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Not implemented"); + File dataFile = taskInfo.getDataFile(); + + response.setHeader("Content-Type", processor.getContentType()); + response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); + response.setHeader("Content-Length", String.valueOf(dataFile.length())); + + try (InputStream is = new FileInputStream(dataFile)) { + IOUtils.copyStream(is, response.getOutputStream()); + } + + dtConfig.removeTask(taskInfo); + + //response.ok(HttpServletResponse.SC_BAD_REQUEST, "Not implemented"); } catch (Exception e) { log.error(e); response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Error reading data: " + e.getMessage()); diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferSessionConfig.java b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferSessionConfig.java index 786266f7d2..22d07c23c5 100644 --- a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferSessionConfig.java +++ b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferSessionConfig.java @@ -5,8 +5,34 @@ import java.util.Map; public class WebDataTransferSessionConfig { - private Map tasks = new HashMap<>(); + private final Map tasks = new HashMap<>(); + public WebDataTransferSessionConfig() { + } + public WebDataTransferTaskConfig getTask(String dataFileId) { + return tasks.get(dataFileId); + } + + public void addTask(WebDataTransferTaskConfig taskConfig) { + synchronized (tasks) { + tasks.put(taskConfig.getDataFileId(), taskConfig); + } + } + + public void removeTask(WebDataTransferTaskConfig taskConfig) { + synchronized (tasks) { + tasks.remove(taskConfig.getDataFileId()); + taskConfig.deleteFile(); + } + } + + public WebDataTransferSessionConfig deleteExportFiles() { + synchronized (tasks) { + tasks.values().forEach(WebDataTransferTaskConfig::deleteFile); + tasks.clear(); + } + return this; + } } \ No newline at end of file diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferStreamProcessor.java b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferStreamProcessor.java index 8031a3df86..7cee0a2a0e 100644 --- a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferStreamProcessor.java +++ b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferStreamProcessor.java @@ -4,9 +4,7 @@ import io.cloudbeaver.server.model.WebPropertyInfo; import io.cloudbeaver.server.model.session.WebSession; import org.jkiss.code.NotNull; import org.jkiss.dbeaver.model.meta.Property; -import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor; import org.jkiss.dbeaver.tools.transfer.registry.DataTransferProcessorDescriptor; -import org.jkiss.utils.CommonUtils; import java.util.Arrays; import java.util.List; @@ -24,7 +22,7 @@ public class WebDataTransferStreamProcessor { @Property public String getId() { - return processor.getId(); + return processor.getFullId(); } @Property @@ -39,8 +37,7 @@ public class WebDataTransferStreamProcessor { @Property public String getFileExtension() { - DBPPropertyDescriptor extProperty = processor.getProperty("extension"); - return extProperty == null ? getAppFileExtension() : CommonUtils.toString(extProperty.getDefaultValue(), null); + return WebDataTransferUtils.getProcessorFileExtension(processor); } @Property diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferTaskConfig.java b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferTaskConfig.java index a8bb976e06..a987c95575 100644 --- a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferTaskConfig.java +++ b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferTaskConfig.java @@ -1,14 +1,45 @@ package io.cloudbeaver.service.data.transfer; -import org.jkiss.dbeaver.tools.transfer.registry.DataTransferProcessorDescriptor; +import org.jkiss.dbeaver.Log; -import java.util.Map; +import java.io.File; public class WebDataTransferTaskConfig { - private String dataFileId; - private DataTransferProcessorDescriptor processor; - private Map settings; - private Map processorProperties; + private static final Log log = Log.getLog(WebDataTransferTaskConfig.class); + private File dataFile; + private WebDataTransferParameters parameters; + private String exportFileName; + + public WebDataTransferTaskConfig(File dataFile, WebDataTransferParameters parameters) { + this.dataFile = dataFile; + this.parameters = parameters; + } + + public File getDataFile() { + return dataFile; + } + + public String getDataFileId() { + return dataFile.getName(); + } + + public WebDataTransferParameters getParameters() { + return parameters; + } + + public String getExportFileName() { + return exportFileName; + } + + public void setExportFileName(String exportFileName) { + this.exportFileName = exportFileName; + } + + public void deleteFile() { + if (!dataFile.delete()) { + log.error("Error deleting export file " + dataFile.getAbsolutePath()); + } + } } \ No newline at end of file diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferUtils.java b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferUtils.java index fab3753805..13a11fb052 100644 --- a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferUtils.java +++ b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebDataTransferUtils.java @@ -1,6 +1,10 @@ package io.cloudbeaver.service.data.transfer; +import io.cloudbeaver.server.model.session.WebSession; import org.jkiss.dbeaver.Log; +import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor; +import org.jkiss.dbeaver.tools.transfer.registry.DataTransferProcessorDescriptor; +import org.jkiss.utils.CommonUtils; import javax.servlet.http.HttpServlet; @@ -9,4 +13,13 @@ public class WebDataTransferUtils extends HttpServlet { private static final Log log = Log.getLog(WebDataTransferUtils.class); + public static String getProcessorFileExtension(DataTransferProcessorDescriptor processor) { + DBPPropertyDescriptor extProperty = processor.getProperty("extension"); + String ext = extProperty == null ? processor.getAppFileExtension() : CommonUtils.toString(extProperty.getDefaultValue(), null); + return CommonUtils.isEmpty(ext) ? "data" : ext; + } + + public static WebDataTransferSessionConfig getSessionDataTransferConfig(WebSession session) { + return session.getAttribute("dataTransfer", x -> new WebDataTransferSessionConfig(), WebDataTransferSessionConfig::deleteExportFiles); + } } \ No newline at end of file diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebServiceDataTransfer.java b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebServiceDataTransfer.java index 3691b73e53..e29ae2d076 100644 --- a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebServiceDataTransfer.java +++ b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebServiceDataTransfer.java @@ -38,6 +38,11 @@ public class WebServiceDataTransfer implements DBWServiceGraphQL, DBWServiceServ private WebDataTransferManager dtManager; + public WebServiceDataTransfer() { + dtManager = new WebDataTransferManager(); + + } + @Override public TypeDefinitionRegistry getTypeDefinition() throws DBWebException { return WebServiceUtils.loadSchemaDefinition(getClass(), DT_SCHEMA_FILE_NAME); @@ -45,25 +50,21 @@ public class WebServiceDataTransfer implements DBWServiceGraphQL, DBWServiceServ @Override public void bindWiring(DBWModel model) { - dtManager = new WebDataTransferManager(model); model.getQueryType() .dataFetcher("dataTransferAvailableStreamProcessors", env -> dtManager.getAvailableStreamProcessors(getWebSession(model, env))) .dataFetcher("dataTransferExportDataFromContainer", env -> dtManager.dataTransferExportDataFromContainer( - getWebSession(model, env), - env.getArgument("connectionId"), + DBWUtils.getSQLProcessor(model.getSessionManager(), env), env.getArgument("containerNodePath"), - env.getArgument("parameters") + new WebDataTransferParameters(env.getArgument("parameters")) )) .dataFetcher("dataTransferExportDataFromResults", env -> dtManager.dataTransferExportDataFromResults( - getWebSession(model, env), - env.getArgument("connectionId"), - env.getArgument("contextId"), + DBWUtils.getSQLContext(model.getSessionManager(), env), env.getArgument("resultsId"), - env.getArgument("parameters") + new WebDataTransferParameters(env.getArgument("parameters")) )) .dataFetcher("dataTransferRemoveDataFile", env -> dtManager.dataTransferRemoveDataFile( - getWebSession(model, env), + DBWUtils.getSQLProcessor(model.getSessionManager(), env), env.getArgument("dataFileId") )) ; @@ -76,7 +77,8 @@ public class WebServiceDataTransfer implements DBWServiceGraphQL, DBWServiceServ @Override public void addServlets(CloudbeaverApplication application, ServletContextHandler servletContextHandler) { - ServletHolder servletHolder = new ServletHolder("dataTransfer", new WebDataTransferServlet(application)); - servletContextHandler.addServlet(servletHolder, application.getServicesURI() + "data/*"); + servletContextHandler.addServlet( + new ServletHolder("dataTransfer", new WebDataTransferServlet(application, dtManager)), + application.getServicesURI() + "data/*"); } }