CB-750 Complex value transfer API

This commit is contained in:
serge-rider
2021-03-14 09:58:40 +03:00
parent 1987ea6b63
commit 12f52bdd22
8 changed files with 241 additions and 118 deletions
@@ -224,7 +224,7 @@ public class CBApplication extends BaseApplicationImpl {
log.debug("\tContent root: " + new File(contentRoot).getAbsolutePath());
log.debug("\tDrivers storage: " + new File(driversLocation).getAbsolutePath());
//log.debug("\tDrivers root: " + driversLocation);
log.debug("\tProduct details: " + application.getInfoDetails());
//log.debug("\tProduct details: " + application.getInfoDetails());
log.debug("\tBase port: " + serverPort);
log.debug("\tBase URI: " + servicesURI);
if (develMode) {
@@ -0,0 +1,46 @@
package io.cloudbeaver.service;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBPlatform;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public abstract class WebServiceServletBase extends HttpServlet {
private static final Log log = Log.getLog(WebServiceServletBase.class);
private final CBApplication application;
public WebServiceServletBase(CBApplication application) {
this.application = application;
}
public CBApplication getApplication() {
return application;
}
@Override
protected final void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
WebSession webSession = CBPlatform.getInstance().getSessionManager().findWebSession(request);
if (webSession == null) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Web session not found");
return;
}
try {
processServiceRequest(webSession, request, response);
} catch (Exception e) {
log.error(e);
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Error processing request: " + e.getMessage());
}
}
protected abstract void processServiceRequest(WebSession session, HttpServletRequest request, HttpServletResponse response) throws DBException, IOException;
}
@@ -0,0 +1,66 @@
package io.cloudbeaver.service.sql;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.service.WebServiceServletBase;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.utils.CommonUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class WebSQLResultServlet extends WebServiceServletBase {
private static final Log log = Log.getLog(WebSQLResultServlet.class);
// context-id/result-id/row-number/attribute-name
private static final Pattern URL_PATTERN = Pattern.compile("/?([\\w]+)/([0-9]+)/([0-9]+)/([0-9]+)/(.+)[/\\?]?");
private final DBWServiceSQL sqlService;
public WebSQLResultServlet(CBApplication application, DBWServiceSQL sqlService) {
super(application);
this.sqlService = sqlService;
}
@Override
protected void processServiceRequest(WebSession session, HttpServletRequest request, HttpServletResponse response) throws DBException, IOException {
String valuePath = request.getPathInfo();
if (CommonUtils.isEmpty(valuePath)) {
throw new DBWebException("Result value ID not specified");
}
Matcher urlMatcher = URL_PATTERN.matcher(valuePath);
if (!urlMatcher.matches()) {
throw new DBWebException("Invalid URI format");
}
String connectionId = urlMatcher.group(1);
String contextId = urlMatcher.group(2);
String resultsId = urlMatcher.group(3);
String rowNum = urlMatcher.group(4);
String attrName = urlMatcher.group(5);
//sqlService.
/*
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());
}
// TODO: cleanup export files ASAP?
if (false) {
dtConfig.removeTask(taskInfo);
}
*/
}
}
@@ -18,6 +18,7 @@ package io.cloudbeaver.service.sql;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.server.CBConstants;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.data.*;
import org.jkiss.dbeaver.model.exec.DBCException;
@@ -25,14 +26,12 @@ import org.jkiss.dbeaver.model.gis.DBGeometry;
import org.jkiss.dbeaver.model.struct.DBSAttributeBase;
import org.jkiss.dbeaver.model.struct.DBSTypedObject;
import org.jkiss.dbeaver.utils.ContentUtils;
import org.jkiss.dbeaver.utils.GeneralUtils;
import org.jkiss.utils.CommonUtils;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.*;
/**
* Web SQL utils.
@@ -41,6 +40,8 @@ public class WebSQLUtils {
private static final Log log = Log.getLog(WebSQLUtils.class);
public static final int BINARY_PREVIEW_LENGTH = 255;
public static Object makeWebCellValue(WebSession session, DBSTypedObject type, Object cellValue, WebDataFormat dataFormat) throws DBCException {
if (cellValue instanceof Date) {
return CBConstants.ISO_DATE_FORMAT.format(cellValue);
@@ -51,11 +52,7 @@ public class WebSQLUtils {
return null;
}
else if (dbValue instanceof DBDDocument) {
if (dataFormat != WebDataFormat.document) {
return serializeDocumentValue((DBDDocument) dbValue);
} else {
return serializeDocumentValue(session, (DBDDocument) dbValue);
}
return serializeDocumentValue(session, (DBDDocument) dbValue);
} else if (dbValue instanceof DBDComplexValue) {
return serializeComplexValue(session, (DBDComplexValue)dbValue, dataFormat);
} else if (dbValue instanceof DBGeometry) {
@@ -75,18 +72,31 @@ public class WebSQLUtils {
for (int i = 0; i < size; i++) {
items[i] = makeWebCellValue(session, collection.getComponentType(), collection.getItem(i), dataFormat);
}
return items;
Map<String, Object> map = createMapOfType("collection");
map.put("value", items);
return map;
} else if (value instanceof DBDComposite) {
DBDComposite composite = (DBDComposite)value;
Map<String, Object> map = new LinkedHashMap<>();
Map<String, Object> struct = new LinkedHashMap<>();
for (DBSAttributeBase attr : composite.getAttributes()) {
map.put(attr.getName(), makeWebCellValue(session, attr, composite.getAttributeValue(attr), dataFormat));
struct.put(attr.getName(), makeWebCellValue(session, attr, composite.getAttributeValue(attr), dataFormat));
}
Map<String, Object> map = createMapOfType("map");
map.put("value", struct);
return map;
}
return value.toString();
}
@NotNull
private static Map<String, Object> createMapOfType(String type) {
Map<String, Object> map = new LinkedHashMap<>();
map.put("$type", type);
return map;
}
private static Map<String, Object> serializeDocumentValue(WebSession session, DBDDocument document) throws DBCException {
String documentData;
try {
@@ -97,7 +107,7 @@ public class WebSQLUtils {
throw new DBCException("Error serializing document", e);
}
Map<String, Object> map = new LinkedHashMap<>();
Map<String, Object> map = createMapOfType("document");
map.put("id", CommonUtils.toString(document.getDocumentId()));
map.put("contentType", document.getDocumentContentType());
map.put("properties", Collections.emptyMap());
@@ -106,15 +116,34 @@ public class WebSQLUtils {
}
private static Object serializeContentValue(WebSession session, DBDContent value) throws DBCException {
return ContentUtils.getContentStringValue(session.getProgressMonitor(), value);
}
private static Object serializeDocumentValue(DBDDocument value) {
return value;
Map<String, Object> map = createMapOfType("content");
if (ContentUtils.isTextContent(value)) {
String stringValue = ContentUtils.getContentStringValue(session.getProgressMonitor(), value);
map.put("text", stringValue);
} else {
map.put("binary", true);
byte[] binaryValue = ContentUtils.getContentBinaryValue(session.getProgressMonitor(), value);
if (binaryValue != null) {
if (binaryValue.length > BINARY_PREVIEW_LENGTH) {
binaryValue = Arrays.copyOf(binaryValue, BINARY_PREVIEW_LENGTH);
}
map.put("text", GeneralUtils.convertToString(binaryValue, 0, binaryValue.length));
} else {
map.put("text", null);
}
}
map.put("contentType", value.getContentType());
map.put("contentLength", value.getContentLength());
return map;
}
private static Object serializeGeometryValue(DBGeometry value) {
return value.toString();
Map<String, Object> map = createMapOfType("geometry");
map.put("srid", value.getSRID());
map.put("text", value.toString());
map.put("properties", value.getProperties());
return map;
}
}
@@ -20,9 +20,13 @@ import graphql.schema.DataFetchingEnvironment;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.model.WebConnectionInfo;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.service.DBWBindingContext;
import io.cloudbeaver.service.DBWServiceBindingServlet;
import io.cloudbeaver.service.WebServiceBindingBase;
import io.cloudbeaver.service.sql.impl.WebServiceSQL;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.jkiss.dbeaver.DBException;
import org.jkiss.utils.CommonUtils;
@@ -34,7 +38,7 @@ import java.util.stream.Collectors;
/**
* Web service implementation
*/
public class WebServiceBindingSQL extends WebServiceBindingBase<DBWServiceSQL> {
public class WebServiceBindingSQL extends WebServiceBindingBase<DBWServiceSQL> implements DBWServiceBindingServlet {
public WebServiceBindingSQL() {
super(DBWServiceSQL.class, new WebServiceSQL(), "schema/service.sql.graphqls");
@@ -133,6 +137,13 @@ public class WebServiceBindingSQL extends WebServiceBindingBase<DBWServiceSQL> {
return context;
}
@Override
public void addServlets(CBApplication application, ServletContextHandler servletContextHandler) {
servletContextHandler.addServlet(
new ServletHolder("sqlResultValueViewer", new WebSQLResultServlet(application, getServiceImpl())),
application.getServicesURI() + "sql-result-value/*");
}
private static class WebSQLConfiguration {
private final Map<WebConnectionInfo, WebSQLProcessor> processors = new HashMap<>();
@@ -4,15 +4,14 @@ import io.cloudbeaver.DBWConstants;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBPlatform;
import io.cloudbeaver.service.WebServiceServletBase;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBConstants;
import org.jkiss.dbeaver.utils.GeneralUtils;
import org.jkiss.dbeaver.utils.MimeTypes;
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;
@@ -20,55 +19,43 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class WebAdminLogsServlet extends HttpServlet {
public class WebAdminLogsServlet extends WebServiceServletBase {
private static final Log log = Log.getLog(WebAdminLogsServlet.class);
private final CBApplication application;
public WebAdminLogsServlet(CBApplication application) {
this.application = application;
super(application);
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
WebSession webSession = CBPlatform.getInstance().getSessionManager().findWebSession(request);
if (webSession == null) {
throw new DBWebException("No active session");
}
if (!webSession.hasPermission(DBWConstants.PERMISSION_ADMIN)) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Logs accessible for admins only");
return;
}
String logId = request.getPathInfo();
String logFileName = ".log";
if ("debug".equals(logId)) {
logFileName = DBConstants.DEBUG_LOG_FILE_NAME;
}
if (logFileName.contains("/") || logFileName.contains("\\")) {
throw new DBWebException("Bad log file name");
}
File logFile = new File(GeneralUtils.getMetadataFolder(), logFileName);
if (!logFile.exists()) {
throw new DBWebException("Log file '" + logFileName + "' not found");
}
response.setHeader("Content-Type", MimeTypes.TEXT_PLAIN);
if (logFileName.equals(".log")) {
logFileName = "server.log";
}
response.setHeader("Content-Disposition", "attachment; filename=\"" + logFileName + "\"");
try (InputStream is = new FileInputStream(logFile)) {
IOUtils.copyStream(is, response.getOutputStream());
}
} catch (Exception e) {
log.error(e);
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Error reading log: " + e.getMessage());
protected void processServiceRequest(WebSession session, HttpServletRequest request, HttpServletResponse response) throws DBException, IOException {
if (!session.hasPermission(DBWConstants.PERMISSION_ADMIN)) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Logs accessible for admins only");
return;
}
String logId = request.getPathInfo();
String logFileName = ".log";
if ("debug".equals(logId)) {
logFileName = DBConstants.DEBUG_LOG_FILE_NAME;
}
if (logFileName.contains("/") || logFileName.contains("\\")) {
throw new DBWebException("Bad log file name");
}
File logFile = new File(GeneralUtils.getMetadataFolder(), logFileName);
if (!logFile.exists()) {
throw new DBWebException("Log file '" + logFileName + "' not found");
}
response.setHeader("Content-Type", MimeTypes.TEXT_PLAIN);
if (logFileName.equals(".log")) {
logFileName = "server.log";
}
response.setHeader("Content-Disposition", "attachment; filename=\"" + logFileName + "\"");
try (InputStream is = new FileInputStream(logFile)) {
IOUtils.copyStream(is, response.getOutputStream());
}
}
}
@@ -1,18 +1,17 @@
package io.cloudbeaver.service.data.transfer.impl;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.server.CBPlatform;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.service.WebServiceServletBase;
import io.cloudbeaver.service.data.transfer.DBWServiceDataTransfer;
import org.jkiss.dbeaver.DBException;
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;
@@ -20,69 +19,56 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class WebDataTransferServlet extends HttpServlet {
public class WebDataTransferServlet extends WebServiceServletBase {
private static final Log log = Log.getLog(WebDataTransferServlet.class);
private final CBApplication application;
private final DBWServiceDataTransfer dtManager;
public WebDataTransferServlet(CBApplication application, DBWServiceDataTransfer dtManager) {
this.application = application;
super(application);
this.dtManager = dtManager;
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
String dataFileId = request.getPathInfo();
if (CommonUtils.isEmpty(dataFileId)) {
throw new DBWebException("Data ID not specified");
}
while (dataFileId.startsWith("/")) {
dataFileId = dataFileId.substring(1);
}
protected void processServiceRequest(WebSession session, HttpServletRequest request, HttpServletResponse response) throws DBException, IOException {
String dataFileId = request.getPathInfo();
if (CommonUtils.isEmpty(dataFileId)) {
throw new DBWebException("Data ID not specified");
}
while (dataFileId.startsWith("/")) {
dataFileId = dataFileId.substring(1);
}
WebSession webSession = CBPlatform.getInstance().getSessionManager().findWebSession(request);
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();
}
WebDataTransferSessionConfig dtConfig = WebDataTransferUtils.getSessionDataTransferConfig(session);
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();
}
File dataFile = taskInfo.getDataFile();
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()));
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());
}
try (InputStream is = new FileInputStream(dataFile)) {
IOUtils.copyStream(is, response.getOutputStream());
}
// TODO: cleanup export files ASAP?
if (false) {
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());
return;
// TODO: cleanup export files ASAP?
if (false) {
dtConfig.removeTask(taskInfo);
}
}
@@ -6,9 +6,7 @@ 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;
public class WebDataTransferUtils extends HttpServlet {
class WebDataTransferUtils {
private static final Log log = Log.getLog(WebDataTransferUtils.class);