diff --git a/backend/framework/src/main/java/org/jumpserver/chen/framework/console/QueryConsole.java b/backend/framework/src/main/java/org/jumpserver/chen/framework/console/QueryConsole.java index c049d80..e8ca951 100644 --- a/backend/framework/src/main/java/org/jumpserver/chen/framework/console/QueryConsole.java +++ b/backend/framework/src/main/java/org/jumpserver/chen/framework/console/QueryConsole.java @@ -32,6 +32,7 @@ import org.jumpserver.chen.framework.i18n.MessageUtils; import org.jumpserver.chen.framework.jms.acl.ACLResult; import org.jumpserver.chen.framework.jms.entity.CommandRecord; +import org.jumpserver.chen.framework.session.Session; import org.jumpserver.chen.framework.session.SessionManager; import org.jumpserver.chen.framework.session.controller.DialogHandle; import org.jumpserver.chen.framework.session.controller.dialog.Button; @@ -55,6 +56,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -72,8 +74,11 @@ public class QueryConsole extends AbstractConsole { static final String QUERY_TRANSACTION_PROBE_FAILED = "QUERY_TRANSACTION_PROBE_FAILED"; static final String QUERY_INSERT_NOT_SUPPORTED = "QUERY_INSERT_NOT_SUPPORTED"; static final String QUERY_DELETE_NOT_SUPPORTED = "QUERY_DELETE_NOT_SUPPORTED"; + static final String CONSOLE_DATA_VIEW_EDIT_NOT_SUPPORTED = "CONSOLE_DATA_VIEW_EDIT_NOT_SUPPORTED"; private final Datasource datasource; + private final boolean consoleMode; + private final int titleSequence; private final TableChangesPreviewService tableChangesPreviewService = new TableChangesPreviewService(); private final TableChangesSaveService tableChangesSaveService = new TableChangesSaveService(); private final QueryDataViewTableEditContextFactory tableEditContextFactory = new QueryDataViewTableEditContextFactory(); @@ -90,27 +95,41 @@ public class QueryConsole extends AbstractConsole { private volatile QueryTransactionStateInspector transactionStateInspector; private volatile SQLExecutePlan currentPlan; private StateManager stateManager; - private final Map dataViews = new HashMap<>(); + private final Map dataViews = new LinkedHashMap<>(); // Manual context changes remain restricted to values returned by the current server-side actuator. private volatile Map allowedContexts = Map.of(); private static final Gson GSON = new Gson(); public QueryConsole(Datasource datasource, WebSocketSession ws, ConsoleContext context) { + this(datasource, ws, context, false); + } + + public QueryConsole( + Datasource datasource, + WebSocketSession ws, + ConsoleContext context, + boolean consoleMode + ) { super(datasource, ws, context); - this.setTitle(String.format(MessageUtils.get("Query") + "-%d", generateConsoleName())); + this.consoleMode = consoleMode; + this.titleSequence = generateConsoleName(consoleMode); + this.setTitle(String.format( + MessageUtils.get(consoleMode ? "Console" : "Query") + "-%d", + this.titleSequence + )); this.datasource = datasource; } - private static int generateConsoleName() { + private static int generateConsoleName(boolean consoleMode) { int num = 1; var consoles = SessionManager .getCurrentSession() .getConsoles(); for (var console : consoles.values()) { - if (console instanceof QueryConsole) { - ++num; + if (console instanceof QueryConsole queryConsole && queryConsole.consoleMode == consoleMode) { + num = Math.max(num, queryConsole.titleSequence + 1); } } return num; @@ -253,9 +272,12 @@ private void handleSerialPacket(Packet packet) { switch (packet.getType()) { case "ping" -> this.getPacketIO().sendPacket("pong", null); case "close_data_view" -> { - var name = (String) packet.getData(); - this.dataViews.remove(name); - log.info("close data view {}", name); + var reference = (String) packet.getData(); + var dataView = this.findDataView(reference); + if (dataView != null) { + this.dataViews.remove(dataView.getId()); + } + log.info("close data view {}", reference); } case Packet.TYPE_QUERY_CONSOLE_ACTION -> { @@ -396,7 +418,25 @@ private void resetSQLChunks() { } private void onDataViewAction(DataViewAction action) { - var dataView = this.dataViews.get(action.getDataView()); + if (this.consoleMode && DataViewAction.ACTION_SAVE_CHANGES_PREVIEW.equals(action.getAction())) { + this.getPacketIO().sendPacket(PACKET_SAVE_CHANGES_PREVIEW_RESULT, Map.of( + "success", false, + "allowed", false, + "reason", CONSOLE_DATA_VIEW_EDIT_NOT_SUPPORTED, + "dataView", action.getDataView() + )); + return; + } + if (this.consoleMode && DataViewAction.ACTION_SAVE_CHANGES.equals(action.getAction())) { + this.getPacketIO().sendPacket(PACKET_SAVE_CHANGES_RESULT, Map.of( + "success", false, + "allowed", false, + "reason", CONSOLE_DATA_VIEW_EDIT_NOT_SUPPORTED, + "dataView", action.getDataView() + )); + return; + } + var dataView = this.findDataView(action.getDataView()); if (dataView == null) { log.error("data view {} not found", action.getDataView()); return; @@ -432,7 +472,10 @@ private void onDataViewAction(DataViewAction action) { dataView.doAction(action); - this.getPacketIO().sendPacket("update_data_view", new UpdateDataView(action.getDataView(), dataView.getData())); + this.getPacketIO().sendPacket( + "update_data_view", + new UpdateDataView(dataView.getId(), dataView.getTitle(), dataView.getData()) + ); } catch (SQLException e) { this.getMessager().send(Message.error(MessageUtils.get("FetchError"), e.getMessage())); @@ -553,7 +596,7 @@ private SaveChangesPreviewResult rejectedPreview(DataView dataView, String reaso result.setSuccess(false); result.setAllowed(false); result.setReason(reason); - result.setDataView(dataView.getTitle()); + result.setDataView(dataView.getId()); return result; } @@ -562,7 +605,7 @@ private SaveChangesResult rejectedSave(DataView dataView, String reason) { result.setSuccess(false); result.setAllowed(false); result.setReason(reason); - result.setDataView(dataView.getTitle()); + result.setDataView(dataView.getId()); return result; } @@ -700,72 +743,14 @@ public void onSQL(String sql) { this.stateManager.commit(); var session = SessionManager.getCurrentSession(); - var aclResult = session.checkACL(sql, this.getConnection()); - if (aclResult != null) { - if (aclResult.getRiskLevel() == Common.RiskLevel.Reject || aclResult.getRiskLevel() == Common.RiskLevel.ReviewReject) { - this.getConsoleLogger().error("%s", MessageUtils.get("ACLRejectError")); - CommandRecord commandRecord = new CommandRecord(sql); - commandRecord.setRiskLevel(aclResult.getRiskLevel()); - session.recordCommand(commandRecord); - - this.getState().setInQuery(false); - this.stateManager.commit(); - return; - } - - if (aclResult.isNotify()) { - - var dialog = new Dialog(MessageUtils.get("Warning")); - dialog.setBody(MessageUtils.get("CommandWarningDialogMessage")); - var countDownLatch = new CountDownLatch(1); - AtomicBoolean hasNext = new AtomicBoolean(true); - - dialog.addButton(new Button(MessageUtils.get("Submit"), "submit", countDownLatch::countDown)); - - dialog.addButton(new Button(MessageUtils.get("Cancel"), "cancel", () -> { - hasNext.set(false); - countDownLatch.countDown(); - this.getConsoleLogger().warn(MessageUtils.get("ExecutionCanceled")); - })); - - var controller = SessionManager.getCurrentSession().getController(); - DialogHandle dialogHandle = controller.showDialog(dialog, () -> { - hasNext.set(false); - countDownLatch.countDown(); - }); - - try { - if (!countDownLatch.await(WARNING_DIALOG_TIMEOUT_SECONDS, TimeUnit.SECONDS)) { - hasNext.set(false); - dialogHandle.cancel(); - this.getConsoleLogger().warn(MessageUtils.get("ExecutionCanceled")); - } - - if (!hasNext.get()) { - this.getState().setInQuery(false); - this.stateManager.commit(); - return; - } - - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - hasNext.set(false); - this.getState().setInQuery(false); - this.stateManager.commit(); - - this.getConsoleLogger().error("获取结果失败!"); - return; - } finally { - dialogHandle.close(); - } - } - } - - try { var stmts = this.getSqlActuator().parseSQL(SQL.of(sql)); var clearOthers = true; for (String stmt : stmts) { + var aclResult = session.checkACL(stmt, this.getConnection()); + if (!this.canExecuteStatement(session, stmt, aclResult)) { + break; + } var dataView = this.runSingleSQL(stmt, aclResult); if (!dataView.isHasTable()) { this.getConsoleLogger().success("%s , %s: %d", @@ -790,6 +775,57 @@ public void onSQL(String sql) { } } + private boolean canExecuteStatement(Session session, String sql, ACLResult aclResult) { + if (aclResult == null) { + return true; + } + if (aclResult.getRiskLevel() == Common.RiskLevel.Reject || + aclResult.getRiskLevel() == Common.RiskLevel.ReviewReject || + aclResult.getRiskLevel() == Common.RiskLevel.ReviewCancel) { + this.getConsoleLogger().error("%s", MessageUtils.get("ACLRejectError")); + CommandRecord commandRecord = new CommandRecord(sql); + commandRecord.setRiskLevel(aclResult.getRiskLevel()); + session.recordCommand(commandRecord); + return false; + } + return !aclResult.isNotify() || this.confirmStatementWarning(session); + } + + private boolean confirmStatementWarning(Session session) { + var dialog = new Dialog(MessageUtils.get("Warning")); + dialog.setBody(MessageUtils.get("CommandWarningDialogMessage")); + var countDownLatch = new CountDownLatch(1); + AtomicBoolean hasNext = new AtomicBoolean(true); + + dialog.addButton(new Button(MessageUtils.get("Submit"), "submit", countDownLatch::countDown)); + dialog.addButton(new Button(MessageUtils.get("Cancel"), "cancel", () -> { + hasNext.set(false); + countDownLatch.countDown(); + this.getConsoleLogger().warn(MessageUtils.get("ExecutionCanceled")); + })); + + var controller = session.getController(); + DialogHandle dialogHandle = controller.showDialog(dialog, () -> { + hasNext.set(false); + countDownLatch.countDown(); + }); + + try { + if (!countDownLatch.await(WARNING_DIALOG_TIMEOUT_SECONDS, TimeUnit.SECONDS)) { + hasNext.set(false); + dialogHandle.cancel(); + this.getConsoleLogger().warn(MessageUtils.get("ExecutionCanceled")); + } + return hasNext.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + this.getConsoleLogger().error("获取结果失败!"); + return false; + } finally { + dialogHandle.close(); + } + } + private SQLActuator getSqlActuator() { return this.getDatasource() .getConnectionManager() @@ -818,7 +854,12 @@ private void ensureCurrentSchema() { private DataView runSingleSQL(String sql, ACLResult aclResult) throws SQLException { String sourceSQL = sql; - DataView dataView = new DataView(sourceSQL, this.getPacketIO(), this.getConsoleLogger()); + DataView dataView = new DataView( + UUID.randomUUID().toString(), + sourceSQL, + this.getPacketIO(), + this.getConsoleLogger() + ); dataView.setSql(sourceSQL); dataView.setLoadDataInterface((sqlQueryParams) -> { @@ -858,26 +899,54 @@ private DataView runSingleSQL(String sql, ACLResult aclResult) throws SQLExcepti private void sendDataView(DataView dataView, boolean clearOthers) { + if (this.consoleMode) { + this.getPacketIO().sendPacket("console_result", Map.of( + "id", dataView.getId(), + "title", dataView.getTitle(), + "data", dataView.getData(), + "state", dataView.getStateManager().getState() + )); + return; + } + if (clearOthers) { - var forDeleteDataViewTitles = new ArrayList(); - for (var title : this.dataViews.keySet()) { - if (!dataView.getTitle().equals(title) && !this.dataViews.get(title).getStateManager().getState().isPinned()) { - forDeleteDataViewTitles.add(title); + var forDeleteDataViewIds = new ArrayList(); + for (var entry : this.dataViews.entrySet()) { + if (!entry.getValue().getStateManager().getState().isPinned()) { + forDeleteDataViewIds.add(entry.getKey()); } } - forDeleteDataViewTitles.forEach(this.dataViews.keySet()::remove); - this.getPacketIO().sendPacket("close_data_view", forDeleteDataViewTitles); + forDeleteDataViewIds.forEach(this.dataViews.keySet()::remove); + this.getPacketIO().sendPacket("close_data_view", forDeleteDataViewIds); } - if (!this.dataViews.containsKey(dataView.getTitle())) { - this.getPacketIO().sendPacket("new_data_view", Map.of("title", dataView.getTitle())); - } + this.getPacketIO().sendPacket( + "new_data_view", + Map.of("id", dataView.getId(), "title", dataView.getTitle()) + ); - this.dataViews.put(dataView.getTitle(), dataView); - this.getPacketIO().sendPacket("update_data_view", new UpdateDataView(dataView.getTitle(), dataView.getData())); + this.dataViews.put(dataView.getId(), dataView); + this.getPacketIO().sendPacket( + "update_data_view", + new UpdateDataView(dataView.getId(), dataView.getTitle(), dataView.getData()) + ); dataView.getStateManager().commit(); } + private DataView findDataView(String reference) { + var dataView = this.dataViews.get(reference); + if (dataView != null) { + return dataView; + } + DataView matched = null; + for (var candidate : this.dataViews.values()) { + if (StringUtils.equals(candidate.getTitle(), reference)) { + matched = candidate; + } + } + return matched; + } + @Override public void close() { diff --git a/backend/framework/src/main/java/org/jumpserver/chen/framework/console/context/ConsoleContextResolver.java b/backend/framework/src/main/java/org/jumpserver/chen/framework/console/context/ConsoleContextResolver.java index acdb09a..611cb78 100644 --- a/backend/framework/src/main/java/org/jumpserver/chen/framework/console/context/ConsoleContextResolver.java +++ b/backend/framework/src/main/java/org/jumpserver/chen/framework/console/context/ConsoleContextResolver.java @@ -37,7 +37,7 @@ public ConsoleContext resolve(String submittedNodeKey, String consoleType) { private static boolean isAllowedNodeType(String consoleType, String nodeType) { return switch (consoleType) { - case Connect.CONSOLE_TYPE_QUERY -> QUERY_NODE_TYPES.contains(nodeType); + case Connect.CONSOLE_TYPE_QUERY, Connect.CONSOLE_TYPE_CONSOLE -> QUERY_NODE_TYPES.contains(nodeType); case Connect.CONSOLE_TYPE_DATA_VIEW -> DATA_VIEW_NODE_TYPES.contains(nodeType); default -> false; }; diff --git a/backend/framework/src/main/java/org/jumpserver/chen/framework/console/dataview/DataView.java b/backend/framework/src/main/java/org/jumpserver/chen/framework/console/dataview/DataView.java index 5280aec..c293740 100644 --- a/backend/framework/src/main/java/org/jumpserver/chen/framework/console/dataview/DataView.java +++ b/backend/framework/src/main/java/org/jumpserver/chen/framework/console/dataview/DataView.java @@ -26,6 +26,7 @@ @EqualsAndHashCode(callSuper = true) @Data public class DataView extends SQLResult { + private final String id; private final String title; private final StateManager stateManager; private LoadDataInterface loadDataInterface; @@ -38,8 +39,13 @@ public class DataView extends SQLResult { private Logger consoleLogger; public DataView(String title, PacketIO packetIO, Logger logger) { + this(title, title, packetIO, logger); + } + + public DataView(String id, String title, PacketIO packetIO, Logger logger) { + this.id = id; this.title = title; - this.state = new DataViewState(title); + this.state = new DataViewState(this.id, title); this.stateManager = new StateManager<>(this.state, packetIO); this.consoleLogger = logger; } diff --git a/backend/framework/src/main/java/org/jumpserver/chen/framework/console/dataview/UpdateDataView.java b/backend/framework/src/main/java/org/jumpserver/chen/framework/console/dataview/UpdateDataView.java index b390f4d..e5f7559 100644 --- a/backend/framework/src/main/java/org/jumpserver/chen/framework/console/dataview/UpdateDataView.java +++ b/backend/framework/src/main/java/org/jumpserver/chen/framework/console/dataview/UpdateDataView.java @@ -4,10 +4,16 @@ @Data public class UpdateDataView { + private String id; private String title; private DataViewData data; public UpdateDataView(String title, DataViewData data) { + this(title, title, data); + } + + public UpdateDataView(String id, String title, DataViewData data) { + this.id = id; this.title = title; this.data = data; } diff --git a/backend/framework/src/main/java/org/jumpserver/chen/framework/console/entity/request/Connect.java b/backend/framework/src/main/java/org/jumpserver/chen/framework/console/entity/request/Connect.java index f339325..acced2d 100644 --- a/backend/framework/src/main/java/org/jumpserver/chen/framework/console/entity/request/Connect.java +++ b/backend/framework/src/main/java/org/jumpserver/chen/framework/console/entity/request/Connect.java @@ -6,6 +6,7 @@ public class Connect { public static final String CONSOLE_TYPE_QUERY = "query"; + public static final String CONSOLE_TYPE_CONSOLE = "console"; public static final String CONSOLE_TYPE_DATA_VIEW = "data_view"; private String nodeKey; diff --git a/backend/framework/src/main/java/org/jumpserver/chen/framework/console/state/DataViewState.java b/backend/framework/src/main/java/org/jumpserver/chen/framework/console/state/DataViewState.java index 4f75ac6..feebef7 100644 --- a/backend/framework/src/main/java/org/jumpserver/chen/framework/console/state/DataViewState.java +++ b/backend/framework/src/main/java/org/jumpserver/chen/framework/console/state/DataViewState.java @@ -8,14 +8,16 @@ @EqualsAndHashCode(callSuper = true) @Data public class DataViewState extends State { + private String id; private int page; private int limit; private int total; private boolean pinned; private boolean paged; - public DataViewState(String title) { + public DataViewState(String id, String title) { super(title); + this.id = id; this.paged = true; this.pinned = false; this.total = 0; diff --git a/backend/framework/src/main/java/org/jumpserver/chen/framework/session/SessionManager.java b/backend/framework/src/main/java/org/jumpserver/chen/framework/session/SessionManager.java index 1b77515..f495e3e 100644 --- a/backend/framework/src/main/java/org/jumpserver/chen/framework/session/SessionManager.java +++ b/backend/framework/src/main/java/org/jumpserver/chen/framework/session/SessionManager.java @@ -1,6 +1,7 @@ package org.jumpserver.chen.framework.session; import lombok.extern.slf4j.Slf4j; +import org.jumpserver.chen.framework.console.Console; import java.util.Map; import java.util.UUID; @@ -31,6 +32,13 @@ public static void unregisterSession(String token) { log.info("session {} unregistered, current session count {}", token, instance.getCurrentSessionCount()); } + public static boolean registerConsole(String token, String consoleId, Console console) { + return instance.store.computeIfPresent(token, (ignored, session) -> { + session.getConsoles().put(consoleId, console); + return session; + }) != null; + } + public int getCurrentSessionCount() { return instance.store.size(); } diff --git a/backend/framework/src/main/java/org/jumpserver/chen/framework/session/impl/BaseSession.java b/backend/framework/src/main/java/org/jumpserver/chen/framework/session/impl/BaseSession.java index 3fe67d9..533cce2 100644 --- a/backend/framework/src/main/java/org/jumpserver/chen/framework/session/impl/BaseSession.java +++ b/backend/framework/src/main/java/org/jumpserver/chen/framework/session/impl/BaseSession.java @@ -23,10 +23,12 @@ import java.sql.Connection; import java.sql.SQLException; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; @Slf4j public class BaseSession implements Session { @@ -44,6 +46,7 @@ public class BaseSession implements Session { @Getter Map consoles = new ConcurrentHashMap<>(); + private final AtomicBoolean closeStarted = new AtomicBoolean(false); @Getter @Setter @@ -201,10 +204,22 @@ public boolean isActive() { @Override public void close() { + if (!this.beginClose()) { + return; + } + this.closeSessionResources(); + } + + protected final boolean beginClose() { + return this.closeStarted.compareAndSet(false, true); + } + + protected final void closeSessionResources() { if (this.getController() != null) { this.getController().cancelAllDialogs(); } SessionManager.unregisterSession(this.getWebToken()); + this.closeConsoles(); this.getDatasource().close(); this.getPacketIO().close(); var path = this.getTempPath(); @@ -213,6 +228,24 @@ public void close() { } } + private void closeConsoles() { + var detached = new ArrayList>(); + while (!this.consoles.isEmpty()) { + for (var entry : this.consoles.entrySet()) { + if (this.consoles.remove(entry.getKey(), entry.getValue())) { + detached.add(Map.entry(entry.getKey(), entry.getValue())); + } + } + } + for (var entry : detached) { + try { + entry.getValue().close(); + } catch (RuntimeException e) { + log.warn("close console failed, consoleId={}", entry.getKey(), e); + } + } + } + @Override public void close(String message, Object... args) { this.close(); diff --git a/backend/framework/src/main/java/org/jumpserver/chen/framework/session/impl/JMSSession.java b/backend/framework/src/main/java/org/jumpserver/chen/framework/session/impl/JMSSession.java index 00d2932..7d40494 100644 --- a/backend/framework/src/main/java/org/jumpserver/chen/framework/session/impl/JMSSession.java +++ b/backend/framework/src/main/java/org/jumpserver/chen/framework/session/impl/JMSSession.java @@ -267,6 +267,13 @@ private void startWaitIdleTime() { @Override public void close() { + if (!this.beginClose()) { + return; + } + this.closeJmsSessionResources(); + } + + private void closeJmsSessionResources() { if (this.getController() != null) { this.getController().cancelAllDialogs(); } @@ -279,23 +286,27 @@ public void close() { } } finally { - super.close(); + super.closeSessionResources(); } } public void close(String message, String reason, Object... args) { + if (!this.beginClose()) { + return; + } SessionManager.setContext(this.getWebToken()); + try { + this.getPacketIO().sendPacket("session_close", null); - this.getPacketIO().sendPacket("session_close", null); - - var dialog = new Dialog(MessageUtils.get("SessionFinished")); - dialog.setBody(MessageUtils.get(message, args)); - this.getController().showDialog(dialog); - - this.recordLifecycle(ServiceOuterClass.SessionLifecycleLogRequest.EventType.AssetConnectFinished, reason); - this.closed = true; + var dialog = new Dialog(MessageUtils.get("SessionFinished")); + dialog.setBody(MessageUtils.get(message, args)); + this.getController().showDialog(dialog); - this.close(); + this.recordLifecycle(ServiceOuterClass.SessionLifecycleLogRequest.EventType.AssetConnectFinished, reason); + this.closed = true; + } finally { + this.closeJmsSessionResources(); + } } private void finishedJmsSession() { diff --git a/backend/framework/src/main/java/org/jumpserver/chen/framework/ws/ConsoleWebSocketHandler.java b/backend/framework/src/main/java/org/jumpserver/chen/framework/ws/ConsoleWebSocketHandler.java index e98b6b5..d296c1b 100644 --- a/backend/framework/src/main/java/org/jumpserver/chen/framework/ws/ConsoleWebSocketHandler.java +++ b/backend/framework/src/main/java/org/jumpserver/chen/framework/ws/ConsoleWebSocketHandler.java @@ -142,7 +142,12 @@ private void onConnectPacket(WebSocketSession session, Packet packet) { Console console = this.createConsole(connect.getType(), webSess.getDatasource(), session, context); if (console != null) { this.setDatabaseContext(console); - webSess.getConsoles().put(session.getId(), console); + var token = (String) session.getAttributes().get("token"); + if (!SessionManager.registerConsole(token, session.getId(), console)) { + console.close(); + this.closeWebSocket(session); + return; + } console.onInit(connect); log.info("User {} open a console ", webSess.getUsername()); } @@ -152,6 +157,7 @@ protected Console createConsole(String type, org.jumpserver.chen.framework.datas WebSocketSession session, ConsoleContext context) { return switch (type) { case Connect.CONSOLE_TYPE_QUERY -> new QueryConsole(datasource, session, context); + case Connect.CONSOLE_TYPE_CONSOLE -> new QueryConsole(datasource, session, context, true); case Connect.CONSOLE_TYPE_DATA_VIEW -> new DataViewConsole(datasource, session, context); default -> null; };