diff --git a/Dockerfile b/Dockerfile index 60f8be90..6796a52b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,9 @@ -FROM java:8 +FROM eclipse-temurin:8-jdk-focal MAINTAINER qianmoQ "shicheng@ttxit.com" # Add datacap RUN mkdir -p /opt/app -COPY dist/datacap-release.tar.gz /opt/app/datacap-release.tar.gz -RUN tar -xvzf /opt/app/datacap-release.tar.gz -C /opt/app/ +ADD dist/datacap-release.tar.gz /opt/app/ WORKDIR /opt/app/datacap CMD sh ./bin/startup.sh diff --git a/plugin/jdbc/kyuubi/pom.xml b/plugin/jdbc/kyuubi/pom.xml index 845acbb6..66392230 100644 --- a/plugin/jdbc/kyuubi/pom.xml +++ b/plugin/jdbc/kyuubi/pom.xml @@ -13,7 +13,7 @@ DataCap plugin for jdbc (Kyuubi) - 1.5.2-incubating + 1.6.0-incubating jdbc-kyuubi diff --git a/server/src/main/java/io/edurt/datacap/server/common/PluginCommon.java b/server/src/main/java/io/edurt/datacap/server/common/PluginCommon.java new file mode 100644 index 00000000..c4b2a5c2 --- /dev/null +++ b/server/src/main/java/io/edurt/datacap/server/common/PluginCommon.java @@ -0,0 +1,23 @@ +package io.edurt.datacap.server.common; + +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.TypeLiteral; +import io.edurt.datacap.spi.Plugin; + +import java.util.Optional; +import java.util.Set; + +public class PluginCommon +{ + private PluginCommon() {} + + public static Optional getPluginByName(Injector injector, String pluginName) + { + Optional pluginOptional = injector.getInstance(Key.get(new TypeLiteral>() {})) + .stream() + .filter(plugin -> plugin.name().equalsIgnoreCase(pluginName)) + .findFirst(); + return pluginOptional; + } +} diff --git a/server/src/main/java/io/edurt/datacap/server/entity/SourceEntity.java b/server/src/main/java/io/edurt/datacap/server/entity/SourceEntity.java index c8f4573a..0a65a80f 100644 --- a/server/src/main/java/io/edurt/datacap/server/entity/SourceEntity.java +++ b/server/src/main/java/io/edurt/datacap/server/entity/SourceEntity.java @@ -78,6 +78,10 @@ public class SourceEntity @Column(name = "create_time", columnDefinition = "datetime default CURRENT_TIMESTAMP()") private Timestamp createTime; + // Add from 1.1.0.20221115 + @Column(name = "_ssl", columnDefinition = "boolean default false") + private Boolean ssl; + @OneToMany(mappedBy = "plugin", cascade = CascadeType.REMOVE, fetch = FetchType.LAZY) @JsonIgnore private List pluginAudits; diff --git a/server/src/main/java/io/edurt/datacap/server/service/impl/ExecuteServiceImpl.java b/server/src/main/java/io/edurt/datacap/server/service/impl/ExecuteServiceImpl.java index 81193ffe..d65bc001 100644 --- a/server/src/main/java/io/edurt/datacap/server/service/impl/ExecuteServiceImpl.java +++ b/server/src/main/java/io/edurt/datacap/server/service/impl/ExecuteServiceImpl.java @@ -1,9 +1,8 @@ package io.edurt.datacap.server.service.impl; import com.google.inject.Injector; -import com.google.inject.Key; -import com.google.inject.TypeLiteral; import io.edurt.datacap.server.audit.AuditPlugin; +import io.edurt.datacap.server.common.PluginCommon; import io.edurt.datacap.server.common.Response; import io.edurt.datacap.server.common.ServiceState; import io.edurt.datacap.server.entity.ExecuteEntity; @@ -15,7 +14,6 @@ import io.edurt.datacap.spi.model.Configure; import org.springframework.stereotype.Service; import java.util.Optional; -import java.util.Set; @Service public class ExecuteServiceImpl @@ -40,10 +38,7 @@ public class ExecuteServiceImpl } SourceEntity entity = entityOptional.get(); - Optional pluginOptional = this.injector.getInstance(Key.get(new TypeLiteral>() {})) - .stream() - .filter(plugin -> plugin.name().equalsIgnoreCase(entity.getType())) - .findFirst(); + Optional pluginOptional = PluginCommon.getPluginByName(this.injector, entity.getType()); if (!pluginOptional.isPresent()) { return Response.failure(ServiceState.PLUGIN_NOT_FOUND); } @@ -55,6 +50,7 @@ public class ExecuteServiceImpl _configure.setUsername(Optional.ofNullable(entity.getUsername())); _configure.setPassword(Optional.ofNullable(entity.getPassword())); _configure.setDatabase(Optional.ofNullable(entity.getDatabase())); + _configure.setSsl(Optional.ofNullable(entity.getSsl())); _configure.setEnv(Optional.ofNullable(configure.getEnv())); _configure.setFormat(configure.getFormat()); plugin.connect(_configure); diff --git a/server/src/main/java/io/edurt/datacap/server/service/impl/SourceServiceImpl.java b/server/src/main/java/io/edurt/datacap/server/service/impl/SourceServiceImpl.java index 0fa31716..be502508 100644 --- a/server/src/main/java/io/edurt/datacap/server/service/impl/SourceServiceImpl.java +++ b/server/src/main/java/io/edurt/datacap/server/service/impl/SourceServiceImpl.java @@ -4,6 +4,7 @@ import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.TypeLiteral; import io.edurt.datacap.server.adapter.PageRequestAdapter; +import io.edurt.datacap.server.common.PluginCommon; import io.edurt.datacap.server.common.Response; import io.edurt.datacap.server.common.ServiceState; import io.edurt.datacap.server.entity.PageEntity; @@ -59,10 +60,7 @@ public class SourceServiceImpl @Override public Response testConnection(SourceEntity configure) { - Optional pluginOptional = this.injector.getInstance(Key.get(new TypeLiteral>() {})) - .stream() - .filter(plugin -> plugin.name().equalsIgnoreCase(configure.getType())) - .findFirst(); + Optional pluginOptional = PluginCommon.getPluginByName(this.injector, configure.getType()); if (!pluginOptional.isPresent()) { return Response.failure(ServiceState.PLUGIN_NOT_FOUND); } @@ -75,6 +73,7 @@ public class SourceServiceImpl _configure.setPassword(Optional.ofNullable(configure.getPassword())); _configure.setDatabase(Optional.ofNullable(configure.getDatabase())); _configure.setEnv(Optional.empty()); + _configure.setSsl(Optional.ofNullable(configure.getSsl())); _configure.setFormat(FormatType.JSON); plugin.connect(_configure); io.edurt.datacap.spi.model.Response response = plugin.execute(plugin.validator()); diff --git a/server/src/main/schema/update_1.1.0.20221115.sql b/server/src/main/schema/update_1.1.0.20221115.sql new file mode 100644 index 00000000..e897e3c0 --- /dev/null +++ b/server/src/main/schema/update_1.1.0.20221115.sql @@ -0,0 +1,2 @@ +ALTER TABLE `datacap`.`source` + ADD COLUMN `_ssl` boolean default false; diff --git a/spi/src/main/java/io/edurt/datacap/spi/adapter/JdbcAdapter.java b/spi/src/main/java/io/edurt/datacap/spi/adapter/JdbcAdapter.java index 670e06e3..1fc74009 100644 --- a/spi/src/main/java/io/edurt/datacap/spi/adapter/JdbcAdapter.java +++ b/spi/src/main/java/io/edurt/datacap/spi/adapter/JdbcAdapter.java @@ -11,13 +11,14 @@ import io.edurt.datacap.spi.model.Time; import lombok.extern.slf4j.Slf4j; import java.sql.Connection; +import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; -import java.sql.Statement; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Objects; @Slf4j @SuppressFBWarnings(value = {"RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE"}, @@ -46,30 +47,51 @@ public class JdbcAdapter Connection connection = (Connection) this.jdbcConnection.getConnection(); JdbcConfigure configure = (JdbcConfigure) this.jdbcConnection.getConfigure(); if (response.getIsConnected()) { - try (Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(content)) { + try (PreparedStatement statement = connection.prepareStatement(content)) { List headers = new ArrayList<>(); List types = new ArrayList<>(); List columns = new ArrayList<>(); - boolean isPresent = true; - JdbcColumn jdbcColumn = new JdbcColumn(resultSet); - while (resultSet.next()) { - ResultSetMetaData metaData = resultSet.getMetaData(); - int columnCount = metaData.getColumnCount(); - List _columns = new ArrayList<>(); - for (int i = 1; i <= columnCount; i++) { - if (isPresent) { - headers.add(metaData.getColumnName(i)); - types.add(metaData.getColumnTypeName(i)); + try (ResultSet resultSet = statement.executeQuery()) { + boolean isPresent = true; + JdbcColumn jdbcColumn = new JdbcColumn(resultSet); + while (resultSet.next()) { + ResultSetMetaData metaData = resultSet.getMetaData(); + int columnCount = metaData.getColumnCount(); + List _columns = new ArrayList<>(); + for (int i = 1; i <= columnCount; i++) { + if (isPresent) { + headers.add(metaData.getColumnName(i)); + types.add(metaData.getColumnTypeName(i)); + } + _columns.add(jdbcColumn.convert(metaData.getColumnTypeName(i), i)); } - _columns.add(jdbcColumn.convert(metaData.getColumnTypeName(i), i)); + isPresent = false; + columns.add(handlerFormatter(configure.getFormat(), headers, _columns)); } - isPresent = false; - columns.add(handlerFormatter(configure.getFormat(), headers, _columns)); } - response.setHeaders(headers); - response.setTypes(types); - response.setColumns(columns); - response.setIsSuccessful(Boolean.TRUE); + catch (SQLException tryUpdateEx) { + if (Objects.equals(tryUpdateEx.getSQLState(), "S1009")) { + try { + headers.add("result"); + types.add(Integer.class.getSimpleName()); + List _columns = new ArrayList<>(); + _columns.add(statement.executeUpdate()); + columns.add(handlerFormatter(configure.getFormat(), headers, _columns)); + } + catch (SQLException updateEx) { + throw new SQLException(updateEx); + } + } + else { + throw new SQLException(tryUpdateEx); + } + } + finally { + response.setHeaders(headers); + response.setTypes(types); + response.setColumns(columns); + response.setIsSuccessful(Boolean.TRUE); + } } catch (SQLException ex) { log.error("Execute content failed content {} exception ", content, ex); diff --git a/spi/src/main/java/io/edurt/datacap/spi/connection/JdbcConnection.java b/spi/src/main/java/io/edurt/datacap/spi/connection/JdbcConnection.java index e57d25ca..3cb99c28 100644 --- a/spi/src/main/java/io/edurt/datacap/spi/connection/JdbcConnection.java +++ b/spi/src/main/java/io/edurt/datacap/spi/connection/JdbcConnection.java @@ -37,13 +37,21 @@ public class JdbcConnection buffer.append("/"); buffer.append(this.jdbcConfigure.getDatabase().get()); } + if (this.jdbcConfigure.getSsl().isPresent()) { + buffer.append(String.format("?ssl=%s", this.jdbcConfigure.getSsl().get())); + } if (this.jdbcConfigure.getEnv().isPresent()) { Map env = this.jdbcConfigure.getEnv().get(); List flatEnv = env.entrySet() .stream() .map(value -> String.format("%s=%s", value.getKey(), value.getValue())) .collect(Collectors.toList()); - buffer.append("?"); + if (!this.jdbcConfigure.getSsl().isPresent()) { + buffer.append("?"); + } + else { + buffer.append("&"); + } buffer.append(String.join("&", flatEnv)); } return buffer.toString(); diff --git a/spi/src/main/java/io/edurt/datacap/spi/model/Configure.java b/spi/src/main/java/io/edurt/datacap/spi/model/Configure.java index f64ce72b..d956ecca 100644 --- a/spi/src/main/java/io/edurt/datacap/spi/model/Configure.java +++ b/spi/src/main/java/io/edurt/datacap/spi/model/Configure.java @@ -21,5 +21,6 @@ public class Configure private Optional password = Optional.empty(); private Optional database = Optional.empty(); private Optional> env = Optional.empty(); + private Optional ssl = Optional.empty(); private FormatType format = FormatType.NONE; } diff --git a/spi/src/test/java/io/edurt/datacap/spi/connection/JdbcConnectionTest.java b/spi/src/test/java/io/edurt/datacap/spi/connection/JdbcConnectionTest.java new file mode 100644 index 00000000..cb7c502a --- /dev/null +++ b/spi/src/test/java/io/edurt/datacap/spi/connection/JdbcConnectionTest.java @@ -0,0 +1,46 @@ +package io.edurt.datacap.spi.connection; + +import io.edurt.datacap.spi.model.Response; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public class JdbcConnectionTest +{ + private JdbcConfigure jdbcConfigure; + private Response response; + + @Before + public void before() + { + this.jdbcConfigure = new JdbcConfigure(); + this.jdbcConfigure.setJdbcType("datacap"); + this.jdbcConfigure.setJdbcDriver("io.edurt.datacap.DataCapDriver"); + this.jdbcConfigure.setHost("127.0.0.1"); + this.jdbcConfigure.setPort(9096); + + this.response = new Response(); + } + + @Test + public void testFormatJdbcUrl() + { + Connection connection = new JdbcConnection(this.jdbcConfigure, this.response); + Assert.assertEquals(connection.formatJdbcUrl(), "jdbc:datacap://127.0.0.1:9096"); + + this.jdbcConfigure.setSsl(Optional.ofNullable(true)); + connection = new JdbcConnection(this.jdbcConfigure, this.response); + Assert.assertEquals(connection.formatJdbcUrl(), "jdbc:datacap://127.0.0.1:9096?ssl=true"); + + Map env = new HashMap<>(); + env.put("useUnicode", "true"); + env.put("zeroDateTimeBehavior", "convertToNull"); + this.jdbcConfigure.setEnv(Optional.ofNullable(env)); + connection = new JdbcConnection(this.jdbcConfigure, this.response); + Assert.assertEquals(connection.formatJdbcUrl(), "jdbc:datacap://127.0.0.1:9096?ssl=true&useUnicode=true&zeroDateTimeBehavior=convertToNull"); + } +} \ No newline at end of file diff --git a/web/console-fe/src/model/SourceModel.ts b/web/console-fe/src/model/SourceModel.ts index 97c39ebc..eb1c7118 100644 --- a/web/console-fe/src/model/SourceModel.ts +++ b/web/console-fe/src/model/SourceModel.ts @@ -12,4 +12,5 @@ export interface SourceModel database: string; type: string; createTime?: number; + ssl?: boolean } diff --git a/web/console-fe/src/views/layout/Layout.vue b/web/console-fe/src/views/layout/Layout.vue index 4384920d..70cf1910 100644 --- a/web/console-fe/src/views/layout/Layout.vue +++ b/web/console-fe/src/views/layout/Layout.vue @@ -21,12 +21,12 @@ - GitHub forks + Forks GitHub forks - GitHub stars + Stars GitHub forks diff --git a/web/console-fe/src/views/pages/admin/source/SourceInfo.vue b/web/console-fe/src/views/pages/admin/source/SourceInfo.vue index fa8ddac2..3669f072 100644 --- a/web/console-fe/src/views/pages/admin/source/SourceInfo.vue +++ b/web/console-fe/src/views/pages/admin/source/SourceInfo.vue @@ -99,6 +99,16 @@ + + + + + +