mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-10-16 14:50:15 +08:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c814017566 | ||
|
|
2a3a94608c | ||
|
|
d810edf89d | ||
|
|
e64d5ff92c | ||
|
|
36be366c64 |
Vendored
+2
-2
@@ -15,11 +15,11 @@
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
"streetsidesoftware.code-spell-checker-russian",
|
||||
"syler.sass-indented",
|
||||
"github.copilot-chat",
|
||||
"VisualStudioExptTeam.vscodeintellicode",
|
||||
"yzhang.markdown-all-in-one",
|
||||
"GraphQL.vscode-graphql-syntax",
|
||||
"mrmlnc.vscode-json5",
|
||||
"esbenp.prettier-vscode",
|
||||
"devnaumov.licensify"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,36 +25,52 @@ You can see a live demo of CloudBeaver here: https://demo.cloudbeaver.io
|
||||
|
||||
## Changelog
|
||||
|
||||
### 25.3.0 2025-12-01
|
||||
### Changes since 25.2.0:
|
||||
- Administration:
|
||||
- Permission configuration for connections has been simplified, providing a clearer appearance and improved performance;
|
||||
- LDAP authentication now supports case-insensitive Distinguished Names (DNs), allowing login with mixed-case DNS.
|
||||
- Security:
|
||||
- Fixed a security issue in the Spatial Viewer for the HTML content to prevent a third-party script execution;
|
||||
- Fixed the critical vulnerability (CVE-2025-61927) in the Happy DOM library. The library was updated to version 20.0.2.
|
||||
- SQL Editor:
|
||||
- A confirmation prompt has been added to the SQL Editor to prevent accidental execution of potentially dangerous queries. You can customize this behavior in Preferences > SQL Editor;
|
||||
- A search field has been added to the catalog/schema selector on the main toolbar for easy navigation;
|
||||
- Alphabetical order has been added to the autocomplete values in the SQL Editor to simplify object search;
|
||||
- Added support for temporary tables in semantic analysis.
|
||||
- Data Editor:
|
||||
- The ability to review the database query after applying filters and sorting in the Data Editor, Grouping panel, and Result sets has been added. The new Show SQL button is placed on the Filter panel;
|
||||
- The value panel in the Data Editor has been redesigned to give users more space for viewing and editing data. Tab buttons for different data type visualizations are now aligned on a single level, creating a cleaner layout;
|
||||
- Added visualization for column reordering in the Data Editor;
|
||||
- Fixed an issue in the Data Editor where resizing one column in a wide table caused previously resized columns to revert to their original width. Users can resize multiple columns as intended.
|
||||
- General:
|
||||
- A reorder tabs option has been added to the application, allowing users to set editor tabs in custom positions;
|
||||
- Keyboard navigation has been improved across the application. Additional shortcuts are available in the main info panel (top-right corner) to help discover them faster. Relevant keyboard shortcuts are also shown in the tooltips of Data Editor buttons;
|
||||
- Boolean values have been replaced with intuitive checkboxes instead of the true/false text in the metadata editor, making editing easier and faster;
|
||||
- The database navigator now automatically hides empty subfolders in shared projects, reducing visual clutter;
|
||||
- Databases and drivers:
|
||||
- ClickHouse: query canceling support has been added;
|
||||
- DuckDB: fixed an issue where catalog selection was lost upon disconnection;
|
||||
- PostgreSQL: fixed support for GIS data types;
|
||||
- SQL Server: the driver has been updated to version 13.2.1;
|
||||
- The "Show all databases" setting has been added on the connection page for MySQL and MariaDB.
|
||||
### 25.2.3 2025-10-20
|
||||
- Added visualization for column reordering in the Data Editor;
|
||||
- Fixed the critical issue with duplicating the execution of queries executed by a shortcut in the SQL Editor;
|
||||
- Fixed the critical vulnerability (CVE-2025-61927) in the Happy DOM library. The library was updated to version 20.0.2.
|
||||
|
||||
### 25.2.2 2025-10-06
|
||||
- Replaced true/false text with intuitive checkboxes for boolean values in the metadata editor, making editing easier and faster;
|
||||
- Keyboard navigation was improved across the application. Additional shortcuts are available in the main info panel (top-right corner). To help you discover them faster, relevant keyboard shortcuts are also shown directly in the tooltips of Data Editor buttons;
|
||||
- Fixed an issue in the Data Editor where resizing one column in a wide table would cause previously resized columns to revert to their original width. Users can resize multiple columns as intended.
|
||||
|
||||
### 25.2.1 2025-09-22
|
||||
- Autocomplete values in SQL Editor are ordered alphabetically to simplify object search;
|
||||
- "Show all databases" setting was added on the connection page for MySQL and MariaDB;
|
||||
- Different bug fixes and enhancements have been made.
|
||||
|
||||
### 25.2.0 2025-09-01
|
||||
### Changes since 25.1.0:
|
||||
- Administration:
|
||||
- Added support for multiple server URLs to accommodate different access policies for internal and external users, improving flexibility in network-restricted environments. To set it up, use the Allowed Server URLs field in the Server configuration within the Administration section;
|
||||
- Added the Force HTTPS mode setting in Server Configuration to enforce redirecting from HTTP to HTTPS. This setting helps avoid a potential man-in-the-middle attack. This setting is turned off by default. Please remember to configure your proxy before enabling;
|
||||
- Added the CLOUDBEAVER_BIND_SESSION_TO_IP option to improve session security by linking user sessions to their IP address. It is disabled by default. When enabled, this helps protect against certain types of session hijacking attacks, where an attacker could try to take over a user’s session. Note: Users will be logged out automatically on the IP address change (switching networks or using mobile data);
|
||||
- A password confirmation field has been added for administrators in the Easy Config section to prevent accidental misconfigurations.
|
||||
- SQL Editor:
|
||||
- Changed the default engine used for autocompletion in the SQL Editor. This Semantic engine offers improved suggestions for database objects, keywords, and functions. You can switch back to the Legacy engine in Preferences > SQL Editor;
|
||||
- A “Clear” button was added to the output panel.
|
||||
- Data Editor:
|
||||
- Column descriptions were added in the Data Editor under column names to provide more metadata context. You can disable this in Preferences > Data Viewer;
|
||||
- Added an ability to review the script before execution when users edit tables without primary keys.
|
||||
- General:
|
||||
- Added a new welcome screen for a freshly opened application. This screen contains shortcuts to create a new connection, open SQL Editor, or documentation;
|
||||
- Added a search panel for SQL Editor and Value panel: press Ctrl/Cmd + F to open a panel that allows searching and replacing by keyword or regular expression;
|
||||
- Changed the data transfer mechanism to avoid intermediate file creation. The parameter dataExportFileSizeLimit was removed from the server configuration as deprecated;
|
||||
- The CloudBeaver default theme matches the device theme by default now. You can change this behavior in user preferences under the Theme section;
|
||||
- Added the option to display tabs across multiple rows, allowing you to see all tabs without scrolling. You can enable this in Preferences > Interface;
|
||||
- Improved dark theme accessibility by enhancing color contrast across the app for better readability and compliance with accessibility standards;
|
||||
- The database navigator now automatically hides empty folders in shared projects, reducing visual clutter and speeding up the process of finding active connections.
|
||||
- Databases and drivers:
|
||||
- Clickhouse: fixed the presentation of tuples and map data types in the Data Editor;
|
||||
- Databend database support has been added (thanks to @hantmac);
|
||||
- DuckDB: driver has been updated to version 1.3;
|
||||
- MySQL: Improved performance when retrieving foreign keys metadata;
|
||||
- PostgreSQL: fixed misplaced comment for table DDL generation.
|
||||
|
||||
## Contribution
|
||||
As a community-driven open-source project, we warmly welcome contributions through GitHub pull requests.
|
||||
|
||||
[We are happy to reward](https://dbeaver.com/help-dbeaver/) our most active contributors every major sprint.
|
||||
The most significant contribution to our code for the major release 25.2.0 was made by:
|
||||
1. [hantmac](https://github.com/hantmac) - added support for Databend in CloudBeaver Community Edition.
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Model
|
||||
Bundle-SymbolicName: io.cloudbeaver.model.cli;singleton:=true
|
||||
Bundle-Version: 1.0.0.qualifier
|
||||
Bundle-Release-Date: 20251222
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: org.jkiss.dbeaver.model.cli;visibility:=reexport
|
||||
Export-Package: io.cloudbeaver.model.cli
|
||||
Automatic-Module-Name: io.cloudbeaver.model.cli
|
||||
Import-Package: org.slf4j
|
||||
@@ -1,5 +0,0 @@
|
||||
source.. = src/
|
||||
output.. = target/classes/
|
||||
bin.includes = .,\
|
||||
META-INF/,\
|
||||
plugin.xml
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?eclipse version="3.2"?>
|
||||
|
||||
<plugin>
|
||||
|
||||
</plugin>
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>io.cloudbeaver</groupId>
|
||||
<artifactId>bundles</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.model.cli</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.cli;
|
||||
|
||||
import picocli.CommandLine;
|
||||
|
||||
/**
|
||||
* CloudBeaver specific mixin options
|
||||
*/
|
||||
public class CloudBeaverMixin {
|
||||
@CommandLine.Option(
|
||||
names = {"-web-config"},
|
||||
arity = "1",
|
||||
hidden = true,
|
||||
scope = CommandLine.ScopeType.INHERIT
|
||||
)
|
||||
private String webConfig;
|
||||
}
|
||||
@@ -3,12 +3,13 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Model
|
||||
Bundle-SymbolicName: io.cloudbeaver.model;singleton:=true
|
||||
Bundle-Version: 1.0.91.qualifier
|
||||
Bundle-Release-Date: 20251222
|
||||
Bundle-Version: 1.0.88.qualifier
|
||||
Bundle-Release-Date: 20251103
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: org.jkiss.dbeaver.data.gis;visibility:=reexport,
|
||||
org.jkiss.dbeaver.model;visibility:=reexport,
|
||||
org.jkiss.dbeaver.model.cli;visibility:=reexport,
|
||||
org.jkiss.dbeaver.model.sm;visibility:=reexport,
|
||||
org.jkiss.dbeaver.model.event;visibility:=reexport,
|
||||
org.jkiss.dbeaver.model.nio;visibility:=reexport,
|
||||
@@ -25,16 +26,17 @@ Export-Package: io.cloudbeaver,
|
||||
io.cloudbeaver.auth.provider.local,
|
||||
io.cloudbeaver.auth.provisioning,
|
||||
io.cloudbeaver.websocket,
|
||||
io.cloudbeaver.websocket.event.client,
|
||||
io.cloudbeaver.websocket.event.task,
|
||||
io.cloudbeaver.model,
|
||||
io.cloudbeaver.model.app,
|
||||
io.cloudbeaver.model.config,
|
||||
io.cloudbeaver.model.apilog,
|
||||
io.cloudbeaver.model.cli,
|
||||
io.cloudbeaver.model.events,
|
||||
io.cloudbeaver.model.fs,
|
||||
io.cloudbeaver.model.log,
|
||||
io.cloudbeaver.model.rm,
|
||||
io.cloudbeaver.model.rm.local,
|
||||
io.cloudbeaver.model.rm.lock,
|
||||
io.cloudbeaver.model.session,
|
||||
io.cloudbeaver.model.user,
|
||||
io.cloudbeaver.model.utils,
|
||||
|
||||
@@ -25,27 +25,4 @@
|
||||
label="Default secret controller" description="Default void secret controller"/>
|
||||
</extension>
|
||||
|
||||
<extension point="org.jkiss.dbeaver.ws.event">
|
||||
<event topicId="cb_session_task" id="cb_session_task_info_updated"
|
||||
class="io.cloudbeaver.websocket.event.task.WSSessionTaskInfoEvent"/>
|
||||
|
||||
<event topicId="cb_session_task" id="cb_session_task_confirmation_request"
|
||||
class="io.cloudbeaver.websocket.event.task.WSSessionTaskConfirmationRequestEvent"/>
|
||||
<event topicId="cb_session_task" id="cb_session_task_query_confirmation_request"
|
||||
class="io.cloudbeaver.websocket.event.task.WSSessionTaskQueryConfirmationRequestEvent"/>
|
||||
<event topicId="cb_session_task" id="cb_session_task_query_params_confirmation_request"
|
||||
class="io.cloudbeaver.websocket.event.task.WSSessionTaskQueryParamsConfirmationEvent"/>
|
||||
|
||||
<clientEvent id="cb_client_session_ping" class="io.cloudbeaver.websocket.event.client.WSSessionPingClientEvent"/>
|
||||
<clientEvent id="cb_client_topic_subscribe" class="io.cloudbeaver.websocket.event.client.WSSubscribeOnTopicClientEvent"/>
|
||||
<clientEvent id="cb_client_topic_unsubscribe"
|
||||
class="io.cloudbeaver.websocket.event.client.WSUnsubscribeFromTopicClientEvent"/>
|
||||
<clientEvent id="cb_client_projects_active"
|
||||
class="io.cloudbeaver.websocket.event.client.WSUpdateActiveProjectsClientEvent"/>
|
||||
<clientEvent id="cb_client_session_task_confirmation"
|
||||
class="io.cloudbeaver.websocket.event.client.WSSessionTaskConfirmationEvent"/>
|
||||
<clientEvent id="cb_client_session_task_with_parameters_confirmation"
|
||||
class="io.cloudbeaver.websocket.event.client.WSSessionTaskWithParametersConfirmationEvent"/>
|
||||
</extension>
|
||||
|
||||
</plugin>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.model</artifactId>
|
||||
<version>1.0.91-SNAPSHOT</version>
|
||||
<version>1.0.88-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+1
-6
@@ -18,7 +18,6 @@ package io.cloudbeaver;
|
||||
|
||||
import io.cloudbeaver.model.WebConnectionConfig;
|
||||
import io.cloudbeaver.model.app.WebAppConfiguration;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.utils.ServletAppUtils;
|
||||
import io.cloudbeaver.utils.WebDataSourceUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
@@ -33,10 +32,8 @@ public class WebConnectionConfigInputHandler<T extends WebConnectionConfig, C ex
|
||||
private static final Log log = Log.getLog(WebConnectionConfigInputHandler.class);
|
||||
protected final T input;
|
||||
protected final DBPDataSourceRegistry registry;
|
||||
protected final WebSession webSession;
|
||||
|
||||
public WebConnectionConfigInputHandler(@NotNull WebSession webSession, @NotNull DBPDataSourceRegistry registry, T configInput) {
|
||||
this.webSession = webSession;
|
||||
public WebConnectionConfigInputHandler(@NotNull DBPDataSourceRegistry registry, T configInput) {
|
||||
this.registry = registry;
|
||||
this.input = configInput;
|
||||
}
|
||||
@@ -55,7 +52,6 @@ public class WebConnectionConfigInputHandler<T extends WebConnectionConfig, C ex
|
||||
}
|
||||
|
||||
WebDataSourceUtils.saveAuthProperties(
|
||||
webSession.getProgressMonitor(),
|
||||
newDataSource,
|
||||
newDataSource.getConnectionConfiguration(),
|
||||
input.getCredentials(),
|
||||
@@ -85,7 +81,6 @@ public class WebConnectionConfigInputHandler<T extends WebConnectionConfig, C ex
|
||||
input
|
||||
);
|
||||
WebDataSourceUtils.saveAuthProperties(
|
||||
webSession.getProgressMonitor(),
|
||||
dataSource,
|
||||
dataSource.getConnectionConfiguration(),
|
||||
input.getCredentials(),
|
||||
|
||||
@@ -244,7 +244,6 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
return sendDataSourceUpdatedEvent;
|
||||
}
|
||||
|
||||
// TODO: load project settings on project load
|
||||
public void refreshProjectSettings() throws DBException {
|
||||
if (webSession.getUser() == null) {
|
||||
projectSettings.clear();
|
||||
@@ -269,7 +268,7 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
if (CommonUtils.isEmpty(configMap)) {
|
||||
throw new DBWebException("Connection configuration parameters are missing");
|
||||
}
|
||||
DBPDataSourceContainer newDataSource = getDataSourceContainerFromInput(getConnectionConfigInput(configMap));
|
||||
DBPDataSourceContainer newDataSource = getDataSourceContainerFromInput(configMap);
|
||||
return addDataSourceToProject(newDataSource);
|
||||
}
|
||||
|
||||
@@ -303,7 +302,7 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
webSession.addInfoMessage("Update connection - " + WebDataSourceUtils.getConnectionContainerInfo(dataSource));
|
||||
|
||||
DBPDataSourceRegistry registry = getDataSourceRegistry();
|
||||
getInputConfigHandler(config).updateDataSource(dataSource);
|
||||
getInputConfigHandler(configMap).updateDataSource(dataSource);
|
||||
connectionInfo.setCredentialsSavedInSession(null);
|
||||
try {
|
||||
registry.updateDataSource(dataSource);
|
||||
@@ -349,8 +348,8 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DataSourceDescriptor getDataSourceContainerFromInput(@NotNull WebConnectionConfig configInput) throws DBWebException {
|
||||
return getInputConfigHandler(configInput).createDataSourceContainer();
|
||||
public DataSourceDescriptor getDataSourceContainerFromInput(@NotNull Map<String, Object> configMap) throws DBWebException {
|
||||
return getInputConfigHandler(configMap).createDataSourceContainer();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -359,8 +358,8 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected WebConnectionConfigInputHandler getInputConfigHandler(@NotNull WebConnectionConfig configInput) {
|
||||
return new WebConnectionConfigInputHandler<>(webSession, getDataSourceRegistry(), configInput);
|
||||
protected WebConnectionConfigInputHandler getInputConfigHandler(@NotNull Map<String, Object> configMap) {
|
||||
return new WebConnectionConfigInputHandler<>(getDataSourceRegistry(), getConnectionConfigInput(configMap));
|
||||
}
|
||||
|
||||
|
||||
|
||||
-4
@@ -74,8 +74,4 @@ public interface SMAuthProviderExternal<AUTH_SESSION extends SMSession> extends
|
||||
*/
|
||||
default void postAuthentication() {}
|
||||
|
||||
@Override
|
||||
default boolean supportsOpeningSessionAsChild() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,16 +341,6 @@ public class WebConnectionInfo {
|
||||
if (CommonUtils.isEmpty(authModelId)) {
|
||||
authModelId = AuthModelDatabaseNative.ID;
|
||||
}
|
||||
|
||||
// Patch auth model
|
||||
DBPAuthModelDescriptor authModel = DBWorkbench.getPlatform().getDataSourceProviderRegistry().getAuthModel(authModelId);
|
||||
if (authModel != null) {
|
||||
DBPAuthModelDescriptor amReplace = authModel.getReplacedBy(dataSourceContainer.getDriver());
|
||||
if (amReplace != null && amReplace != authModel) {
|
||||
return amReplace.getId();
|
||||
}
|
||||
}
|
||||
|
||||
return authModelId;
|
||||
}
|
||||
|
||||
|
||||
+19
-5
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package io.cloudbeaver.model.app;
|
||||
|
||||
import io.cloudbeaver.model.cli.CloudBeaverInstanceServer;
|
||||
import io.cloudbeaver.model.log.SLF4JLogHandler;
|
||||
import org.eclipse.core.runtime.Platform;
|
||||
import org.eclipse.equinox.app.IApplicationContext;
|
||||
@@ -27,6 +28,7 @@ import org.jkiss.dbeaver.model.DBFileController;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
|
||||
import org.jkiss.dbeaver.model.auth.SMSessionContext;
|
||||
import org.jkiss.dbeaver.model.cli.ApplicationInstanceController;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.model.impl.app.ApplicationRegistry;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseApplicationImpl;
|
||||
@@ -39,6 +41,7 @@ import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
import org.jkiss.dbeaver.utils.RuntimeUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
@@ -57,7 +60,7 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
private static final Log log = Log.getLog(BaseServletApplication.class);
|
||||
|
||||
private String instanceId;
|
||||
|
||||
private CloudBeaverInstanceServer instanceServer;
|
||||
@Override
|
||||
public RMController createResourceController(
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@@ -160,7 +163,6 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
* Method returns VoidSecretController instance.
|
||||
* Advanced apps may implement it differently.
|
||||
*/
|
||||
@NotNull
|
||||
@Override
|
||||
public DBSSecretController getSecretController(
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@@ -202,6 +204,11 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
public Object start(IApplicationContext context) {
|
||||
initializeApplicationServices();
|
||||
try {
|
||||
try {
|
||||
this.instanceServer = createInstanceServer();
|
||||
} catch (Exception e) {
|
||||
log.error("Error initializing instance server", e);
|
||||
}
|
||||
startServer();
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
@@ -210,9 +217,12 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
protected CloudBeaverInstanceServer createInstanceServer() throws IOException {
|
||||
return new CloudBeaverInstanceServer();
|
||||
}
|
||||
|
||||
protected abstract void startServer() throws DBException;
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public synchronized String getApplicationInstanceId() throws DBException {
|
||||
if (instanceId == null) {
|
||||
@@ -237,7 +247,6 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
return BaseWorkspaceImpl.readWorkspaceIdProperty();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Path getWorkspaceDirectory() {
|
||||
return getServerConfigurationController().getWorkspacePath();
|
||||
@@ -252,7 +261,6 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public WSEventController getEventController() {
|
||||
return null;
|
||||
@@ -273,6 +281,12 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ApplicationInstanceController getInstanceServer() {
|
||||
return instanceServer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnonymousAccessEnabled() {
|
||||
return getAppConfiguration().isAnonymousAccessEnabled();
|
||||
|
||||
+3
-17
@@ -17,7 +17,6 @@
|
||||
package io.cloudbeaver.model.app;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.DBFileController;
|
||||
import org.jkiss.dbeaver.model.app.DBPApplication;
|
||||
@@ -43,19 +42,14 @@ public interface ServletApplication extends DBPApplication {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
ServletAppConfiguration getAppConfiguration();
|
||||
|
||||
@NotNull
|
||||
ServletServerConfiguration getServerConfiguration();
|
||||
|
||||
@NotNull
|
||||
Path getDataDirectory(boolean create);
|
||||
|
||||
@NotNull
|
||||
Path getWorkspaceDirectory();
|
||||
|
||||
@NotNull
|
||||
Path getHomeDirectory();
|
||||
|
||||
boolean isMultiNode();
|
||||
@@ -64,38 +58,30 @@ public interface ServletApplication extends DBPApplication {
|
||||
|
||||
SMAdminController getAdminSecurityController(@NotNull SMCredentialsProvider credentialsProvider) throws DBException;
|
||||
|
||||
@NotNull
|
||||
DBSSecretController getSecretController(
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
SMSessionContext smSessionContext
|
||||
) throws DBException;
|
||||
|
||||
@NotNull
|
||||
RMController createResourceController(
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@NotNull DBPWorkspace workspace
|
||||
) throws DBException;
|
||||
|
||||
@NotNull
|
||||
DBFileController createFileController(@NotNull SMCredentialsProvider credentialsProvider);
|
||||
|
||||
@Nullable
|
||||
String getServerURL();
|
||||
|
||||
@NotNull
|
||||
default String getServicesURI() {
|
||||
return "/";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
default String getRootURI() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
String getApplicationInstanceId() throws DBException;
|
||||
|
||||
@NotNull
|
||||
WSEventController getEventController();
|
||||
|
||||
/**
|
||||
@@ -108,10 +94,10 @@ public interface ServletApplication extends DBPApplication {
|
||||
/**
|
||||
* Collector that contains information about system.
|
||||
*/
|
||||
@Nullable
|
||||
ServletSystemInformationCollector<?> getSystemInformationCollector();
|
||||
@NotNull
|
||||
ServletSystemInformationCollector getSystemInformationCollector();
|
||||
|
||||
default void getStatusInfo(@NotNull Map<String, Object> infoMap) {
|
||||
default void getStatusInfo(Map<String, Object> infoMap) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
+2
-14
@@ -23,23 +23,13 @@ import org.jkiss.dbeaver.model.cli.model.CommandLineAuthenticator;
|
||||
import picocli.CommandLine;
|
||||
|
||||
public class CloudBeaverCommandLine extends ApplicationCommandLine<ApplicationInstanceController> {
|
||||
|
||||
@Nullable
|
||||
private final CommandLineAuthenticator authenticator;
|
||||
private final CloudBeaverMixin mixin;
|
||||
|
||||
public CloudBeaverCommandLine(@Nullable CommandLineAuthenticator authenticator) {
|
||||
super();
|
||||
this.authenticator = authenticator;
|
||||
this.mixin = new CloudBeaverMixin();
|
||||
}
|
||||
|
||||
public CloudBeaverCommandLine(
|
||||
@Nullable CommandLineAuthenticator authenticator,
|
||||
@NotNull CloudBeaverMixin mixin
|
||||
) {
|
||||
super();
|
||||
this.authenticator = authenticator;
|
||||
this.mixin = mixin;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,8 +51,6 @@ public class CloudBeaverCommandLine extends ApplicationCommandLine<ApplicationIn
|
||||
@NotNull CommandLineContext context,
|
||||
@NotNull CLIRunMeta runMeta
|
||||
) {
|
||||
var cmd = super.initCommandLine(applicationInstanceController, context, runMeta);
|
||||
cmd.addMixin("cloudbeaver", mixin);
|
||||
return cmd;
|
||||
return super.initCommandLine(applicationInstanceController, context, runMeta);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -32,7 +32,7 @@ public class CloudBeaverInstanceServer extends ApplicationInstanceServer<Applica
|
||||
this.commandLine = new CloudBeaverCommandLine(null);
|
||||
}
|
||||
|
||||
public CloudBeaverInstanceServer(@NotNull CloudBeaverCommandLine commandLine) throws IOException {
|
||||
protected CloudBeaverInstanceServer(@NotNull CloudBeaverCommandLine commandLine) throws IOException {
|
||||
super(ApplicationInstanceController.class);
|
||||
this.commandLine = commandLine;
|
||||
}
|
||||
+1
-8
@@ -24,15 +24,8 @@ import org.jkiss.dbeaver.model.cli.CommandLineContext;
|
||||
import org.jkiss.dbeaver.model.cli.command.AbstractTopLevelCommand;
|
||||
import picocli.CommandLine;
|
||||
|
||||
@CommandLine.Command(name = "cbvr", description = "CloudBeaver commands")
|
||||
@CommandLine.Command(name = "cbvr", description = "CloudBeaver commands", mixinStandardHelpOptions = true)
|
||||
public class CloudBeaverTopLevelCommand extends AbstractTopLevelCommand {
|
||||
@CommandLine.Option(
|
||||
names = {NOSPASH_OPTION},
|
||||
hidden = true,
|
||||
scope = CommandLine.ScopeType.INHERIT
|
||||
)
|
||||
private boolean noSplash;
|
||||
|
||||
protected CloudBeaverTopLevelCommand(
|
||||
@Nullable ApplicationInstanceController controller,
|
||||
@NotNull CommandLineContext context,
|
||||
+5
-3
@@ -54,12 +54,12 @@ import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
import org.jkiss.utils.IOUtils;
|
||||
import org.jkiss.utils.Pair;
|
||||
import org.jkiss.utils.StandardConstants;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.text.MessageFormat;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -1262,8 +1262,10 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
project.setProjectPermissions(allProjectPermissions);
|
||||
if (Files.exists(path)) {
|
||||
try {
|
||||
project.setCreateTime(
|
||||
OffsetDateTime.ofInstant(Files.getLastModifiedTime(path).toInstant(), ZoneId.of("UTC")).toInstant().toEpochMilli());
|
||||
project.setCreateTime(OffsetDateTime.ofInstant(
|
||||
Files.getLastModifiedTime(path).toInstant(),
|
||||
StandardConstants.ZONE_ID_UTC
|
||||
).toInstant().toEpochMilli());
|
||||
} catch (IOException e) {
|
||||
log.error(e);
|
||||
}
|
||||
|
||||
+7
-10
@@ -41,7 +41,6 @@ public class WebAuthInfo implements SMSessionPrincipal, WebUserAuthToken {
|
||||
|
||||
private final WebSession session;
|
||||
private final WebUser user;
|
||||
@NotNull
|
||||
private final WebAuthProviderDescriptor authProvider;
|
||||
private String authProviderConfigurationId;
|
||||
@NotNull
|
||||
@@ -72,7 +71,6 @@ public class WebAuthInfo implements SMSessionPrincipal, WebUserAuthToken {
|
||||
this.loginTime = loginTime;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Property
|
||||
public OffsetDateTime getLoginTime() {
|
||||
return loginTime;
|
||||
@@ -93,19 +91,16 @@ public class WebAuthInfo implements SMSessionPrincipal, WebUserAuthToken {
|
||||
return new WebUserOriginInfo(session, user, authProvider);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Property
|
||||
public String getUserId() {
|
||||
return userIdentity.getId();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Property
|
||||
public String getDisplayName() {
|
||||
return userIdentity.getDisplayName();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Property
|
||||
public String getAuthProvider() {
|
||||
return authProvider.getId();
|
||||
@@ -143,11 +138,13 @@ public class WebAuthInfo implements SMSessionPrincipal, WebUserAuthToken {
|
||||
}
|
||||
|
||||
void closeAuth() {
|
||||
try {
|
||||
SMAuthProvider authProviderInstance = this.authProvider.getInstance();
|
||||
authProviderInstance.closeSession(session, authSession);
|
||||
} catch (Exception e) {
|
||||
log.error(e);
|
||||
if (authProvider != null && authSession != null) {
|
||||
try {
|
||||
SMAuthProvider authProviderInstance = this.authProvider.getInstance();
|
||||
authProviderInstance.closeSession(session, authSession);
|
||||
} catch (Exception e) {
|
||||
log.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-1
@@ -51,7 +51,6 @@ public class WebHttpRequestInfo {
|
||||
this.lastRemoteUserAgent = lastRemoteUserAgent;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
+39
-75
@@ -45,6 +45,7 @@ import org.jkiss.dbeaver.model.access.DBACredentialsProvider;
|
||||
import org.jkiss.dbeaver.model.auth.*;
|
||||
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
|
||||
import org.jkiss.dbeaver.model.exec.DBCException;
|
||||
import org.jkiss.dbeaver.model.fs.DBFFileSystemManager;
|
||||
import org.jkiss.dbeaver.model.meta.Association;
|
||||
import org.jkiss.dbeaver.model.meta.Property;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNModel;
|
||||
@@ -65,7 +66,6 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -81,6 +81,7 @@ public class WebSession extends BaseWebSession
|
||||
|
||||
public static final SMSessionType CB_SESSION_TYPE = new SMSessionType("CloudBeaver");
|
||||
private static final String WEB_SESSION_AUTH_CONTEXT_TYPE = "web-session";
|
||||
private static final String ATTR_LOCALE = "locale";
|
||||
private static final AtomicInteger TASK_ID = new AtomicInteger();
|
||||
|
||||
public static String RUNTIME_PARAM_AUTH_INFOS = "auth-infos";
|
||||
@@ -103,16 +104,15 @@ public class WebSession extends BaseWebSession
|
||||
|
||||
private DBNModel navigatorModel;
|
||||
private final DBRProgressMonitor progressMonitor = new SessionProgressMonitor();
|
||||
private final Map<String, DBWSessionHandler<WebSession>> sessionHandlers;
|
||||
private final Map<String, DBWSessionHandler> sessionHandlers;
|
||||
private final WebDataSourceConnectEventListener connectListener = new WebDataSourceConnectEventListener(this);
|
||||
|
||||
public WebSession(
|
||||
@NotNull WebHttpRequestInfo requestInfo,
|
||||
@NotNull ServletAuthApplication application,
|
||||
@NotNull Map<String, DBWSessionHandler<WebSession>> sessionHandlers
|
||||
@NotNull Map<String, DBWSessionHandler> sessionHandlers
|
||||
) throws DBException {
|
||||
this(
|
||||
Objects.requireNonNull(requestInfo.getId()),
|
||||
this(requestInfo.getId(),
|
||||
CommonUtils.toString(requestInfo.getLocale()),
|
||||
application,
|
||||
sessionHandlers,
|
||||
@@ -125,8 +125,8 @@ public class WebSession extends BaseWebSession
|
||||
@NotNull String id,
|
||||
@Nullable String locale,
|
||||
@NotNull ServletApplication application,
|
||||
@NotNull Map<String, DBWSessionHandler<WebSession>> sessionHandlers,
|
||||
@Nullable String remoteAddr
|
||||
@NotNull Map<String, DBWSessionHandler> sessionHandlers,
|
||||
@NotNull String remoteAddr
|
||||
) throws DBException {
|
||||
super(id, application);
|
||||
if (CommonUtils.isEmpty(remoteAddr)) {
|
||||
@@ -151,7 +151,7 @@ public class WebSession extends BaseWebSession
|
||||
if (authTokens.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return authTokens.getFirst();
|
||||
return authTokens.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,24 +254,18 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
|
||||
// Note: for admin use only
|
||||
public synchronized void resetUserState(boolean needResetUserCache) throws DBException {
|
||||
public synchronized void resetUserState() throws DBException {
|
||||
clearAuthTokens();
|
||||
if (needResetUserCache) {
|
||||
try {
|
||||
resetSessionCache();
|
||||
} catch (DBCException e) {
|
||||
addSessionError(e);
|
||||
log.error(e);
|
||||
}
|
||||
try {
|
||||
resetSessionCache();
|
||||
} catch (DBCException e) {
|
||||
addSessionError(e);
|
||||
log.error(e);
|
||||
}
|
||||
refreshUserData();
|
||||
clearSessionContext();
|
||||
}
|
||||
|
||||
public synchronized void resetUserState() throws DBException {
|
||||
resetUserState(true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DBPEventListener getDataSourceConnectListener() {
|
||||
return connectListener;
|
||||
@@ -315,7 +309,7 @@ public class WebSession extends BaseWebSession
|
||||
anonymousProject.setInMemory(true);
|
||||
}
|
||||
if (workspace.getActiveProject() == null && !workspace.getProjects().isEmpty()) {
|
||||
workspace.setActiveProject(workspace.getProjects().getFirst());
|
||||
workspace.setActiveProject(workspace.getProjects().get(0));
|
||||
}
|
||||
} catch (DBException e) {
|
||||
addSessionError(e);
|
||||
@@ -330,6 +324,7 @@ public class WebSession extends BaseWebSession
|
||||
} else {
|
||||
sessionProject = createSessionProject(project);
|
||||
}
|
||||
sessionProject.refreshProjectSettings();
|
||||
// do not load data sources for anonymous project
|
||||
if (project.getType() == RMProjectType.USER && userContext.getUser() == null) {
|
||||
sessionProject.setInMemory(true);
|
||||
@@ -445,7 +440,7 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
|
||||
public synchronized void updateInfo(boolean isOldHttpSessionUsed) {
|
||||
log.trace("Update session lifetime " + getSessionId() + " for user " + getUserId());
|
||||
log.debug("Update session lifetime " + getSessionId() + " for user " + getUserId());
|
||||
touchSession();
|
||||
if (isOldHttpSessionUsed) {
|
||||
try {
|
||||
@@ -564,10 +559,6 @@ public class WebSession extends BaseWebSession
|
||||
if (job instanceof CustomCancelableJob cancelableJob) {
|
||||
cancelableJob.cancelJob(this, taskInfo);
|
||||
}
|
||||
CompletableFuture<?> future = getAttribute(getTaskConfirmationAttributeName(taskId));
|
||||
if (future != null) {
|
||||
future.cancel(false);
|
||||
}
|
||||
if (job != null) {
|
||||
job.cancel();
|
||||
}
|
||||
@@ -577,7 +568,8 @@ public class WebSession extends BaseWebSession
|
||||
|
||||
public WebAsyncTaskInfo createAsyncTask(@NotNull String taskName) {
|
||||
int taskId = TASK_ID.incrementAndGet();
|
||||
return getAsyncTask(String.valueOf(taskId), taskName, true);
|
||||
WebAsyncTaskInfo asyncTask = getAsyncTask(String.valueOf(taskId), taskName, true);
|
||||
return asyncTask;
|
||||
}
|
||||
|
||||
public List<WebAsyncTaskInfo> findTasksByJob(@NotNull Class<? extends AbstractJob> jobClass) {
|
||||
@@ -594,14 +586,10 @@ public class WebSession extends BaseWebSession
|
||||
|
||||
public WebAsyncTaskInfo createAndRunAsyncTask(@NotNull String taskName, @NotNull WebAsyncTaskProcessor<?> runnable) {
|
||||
WebAsyncTaskInfo asyncTask = createAsyncTask(taskName);
|
||||
return runAsyncTask(asyncTask, runnable);
|
||||
}
|
||||
|
||||
public WebAsyncTaskInfo runAsyncTask(@NotNull WebAsyncTaskInfo asyncTask, @NotNull WebAsyncTaskProcessor<?> runnable) {
|
||||
AbstractJob job = new AbstractJob(asyncTask.getName()) {
|
||||
@NotNull
|
||||
AbstractJob job = new AbstractJob(taskName) {
|
||||
@Override
|
||||
protected IStatus run(@NotNull DBRProgressMonitor monitor) {
|
||||
protected IStatus run(DBRProgressMonitor monitor) {
|
||||
int curTaskCount = taskCount.incrementAndGet();
|
||||
|
||||
DBRProgressMonitor taskMonitor = new TaskProgressMonitor(monitor, WebSession.this, asyncTask);
|
||||
@@ -732,7 +720,7 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return authTokens.isEmpty() ? null : authTokens.getFirst();
|
||||
return authTokens.isEmpty() ? null : authTokens.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -775,7 +763,10 @@ public class WebSession extends BaseWebSession
|
||||
if (oldAuthInfo != null) {
|
||||
removeAuthInfo(oldAuthInfo);
|
||||
}
|
||||
getSessionContext().addSession(authInfo.getAuthSession());
|
||||
SMSession authSession = authInfo.getAuthSession();
|
||||
if (authSession != null) {
|
||||
getSessionContext().addSession(authSession);
|
||||
}
|
||||
}
|
||||
synchronized (authTokens) {
|
||||
Collections.addAll(authTokens, tokens);
|
||||
@@ -851,7 +842,7 @@ public class WebSession extends BaseWebSession
|
||||
// uncommented because we had the problem with non-native auth models
|
||||
// (for example, can't connect to DynamoDB if credentials are not saved)
|
||||
DBAAuthCredentials credentials = configuration.getAuthModel().loadCredentials(dataSourceContainer, configuration);
|
||||
WebDataSourceUtils.updateCredentialsFromProperties(this.progressMonitor, credentials, configuration.getAuthProperties());
|
||||
WebDataSourceUtils.updateCredentialsFromProperties(credentials, configuration.getAuthProperties());
|
||||
|
||||
configuration.getAuthModel().provideCredentials(dataSourceContainer, configuration, credentials);
|
||||
} catch (DBException e) {
|
||||
@@ -891,7 +882,7 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
|
||||
private <T> boolean isAuthInfoInstanceOf(WebAuthInfo authInfo, Class<T> adapter) {
|
||||
if (authInfo != null) {
|
||||
if (authInfo != null && authInfo.getAuthSession() != null) {
|
||||
return adapter.isInstance(authInfo.getAuthSession());
|
||||
}
|
||||
return false;
|
||||
@@ -1009,6 +1000,15 @@ public class WebSession extends BaseWebSession
|
||||
deleteSessionProject(project);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DBFFileSystemManager getFileSystemManager(String projectId) throws DBException {
|
||||
var project = getProjectById(projectId);
|
||||
if (project == null) {
|
||||
throw new DBException("Project not found: " + projectId);
|
||||
}
|
||||
return project.getFileSystemManager();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public WebSessionPreferenceStore getUserPreferenceStore() {
|
||||
return getUserContext().getPreferenceStore();
|
||||
@@ -1019,50 +1019,14 @@ public class WebSession extends BaseWebSession
|
||||
return globalProject;
|
||||
}
|
||||
|
||||
public void handleTaskConfirmation(
|
||||
@NotNull String taskId,
|
||||
boolean confirmed,
|
||||
boolean skipConfirmations
|
||||
) {
|
||||
String attributeName = getTaskConfirmationAttributeName(taskId);
|
||||
if (confirmed && skipConfirmations) {
|
||||
setAttribute(WebSQLConstants.SKIP_TASK_CONFIRMATIONS_ATTR, Boolean.TRUE);
|
||||
}
|
||||
|
||||
CompletableFuture<Boolean> confirmationFuture = getAttribute(attributeName);
|
||||
if (confirmationFuture != null) {
|
||||
confirmationFuture.complete(confirmed);
|
||||
removeAttribute(attributeName);
|
||||
} else {
|
||||
log.error("Received unexpected confirmation event for taskId: " + taskId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void handleTaskConfirmationWithParameters(@NotNull String taskId, @NotNull Map<String, Object> parameters) {
|
||||
String attributeName = getTaskConfirmationAttributeName(taskId);
|
||||
CompletableFuture<Map<String, Object>> confirmationFuture = getAttribute(attributeName);
|
||||
if (confirmationFuture != null) {
|
||||
confirmationFuture.complete(parameters);
|
||||
removeAttribute(attributeName);
|
||||
} else {
|
||||
log.error("Received unexpected confirmation event for taskId: " + taskId);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getTaskConfirmationAttributeName(@NotNull String taskId) {
|
||||
return WebSQLConstants.TASK_CONFIRMATION_ATTR_PREFIX + taskId;
|
||||
}
|
||||
|
||||
private class SessionProgressMonitor extends BaseProgressMonitor {
|
||||
@Override
|
||||
public void beginTask(@NotNull String name, int totalWork) {
|
||||
public void beginTask(String name, int totalWork) {
|
||||
addInfoMessage(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subTask(@NotNull String name) {
|
||||
public void subTask(String name) {
|
||||
addInfoMessage(name);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ public class WebSessionAuthProcessor {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (resetUserStateOnError) {
|
||||
webSession.resetUserState(false);
|
||||
webSession.resetUserState();
|
||||
}
|
||||
throw new DBException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
-10
@@ -20,7 +20,6 @@ import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.impl.preferences.AbstractUserPreferenceStore;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
@@ -86,13 +85,4 @@ public class WebSessionPreferenceStore extends AbstractUserPreferenceStore {
|
||||
public void save() throws IOException {
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
|
||||
public boolean getUserPreferenceBoolean(@NotNull String name, boolean fallbackValue) {
|
||||
String value = CommonUtils.toString(userPreferences.get(name));
|
||||
if (value.isEmpty()) {
|
||||
return fallbackValue;
|
||||
} else {
|
||||
return toBoolean(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -109,7 +109,7 @@ public class WebSessionWorkspace implements DBPWorkspace {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public WebSessionProjectImpl getProjectById(@Nullable String projectId) {
|
||||
public WebSessionProjectImpl getProjectById(@NotNull String projectId) {
|
||||
if (projectId == null) {
|
||||
return activeProject;
|
||||
}
|
||||
|
||||
+4
-9
@@ -63,14 +63,11 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
private SMAdminController adminSecurityController;
|
||||
private DBSSecretController secretController;
|
||||
private RMController rmController;
|
||||
private final DBFileController fileController;
|
||||
private final Set<String> accessibleProjectIds = new HashSet<>();
|
||||
private DBFileController fileController;
|
||||
private Set<String> accessibleProjectIds = new HashSet<>();
|
||||
private final WebSessionPreferenceStore preferenceStore;
|
||||
|
||||
public WebUserContext(
|
||||
@NotNull ServletApplication application,
|
||||
@NotNull DBPWorkspace workspace
|
||||
) throws DBException {
|
||||
public WebUserContext(ServletApplication application, DBPWorkspace workspace) throws DBException {
|
||||
this.application = application;
|
||||
this.workspace = workspace;
|
||||
this.securityController = application.createSecurityController(this);
|
||||
@@ -87,7 +84,7 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
* @return - true if context changed
|
||||
* @throws DBException - if user already authorized and new token come from another user
|
||||
*/
|
||||
public synchronized boolean refresh(@NotNull SMAuthInfo smAuthInfo) throws DBException {
|
||||
public synchronized boolean refresh(SMAuthInfo smAuthInfo) throws DBException {
|
||||
if (smAuthInfo.getAuthPermissions() == null && !isAuthorizedInSecurityManager()) {
|
||||
throw new DBCException("Required information about session permissions is missing");
|
||||
}
|
||||
@@ -245,12 +242,10 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
return smSessionId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Set<String> getDefaultPermissions() {
|
||||
return application.isAnonymousAccessEnabled() ? null : Set.of();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public RMController getRmController() {
|
||||
return rmController;
|
||||
}
|
||||
|
||||
+2
-2
@@ -40,14 +40,14 @@ public class TaskProgressMonitor extends ProxyProgressMonitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginTask(@NotNull String name, int totalWork) {
|
||||
public void beginTask(String name, int totalWork) {
|
||||
super.beginTask(name, totalWork);
|
||||
asyncTask.setStatus(name);
|
||||
WebEventUtils.sendAsyncTaskEvent(webSession, asyncTask);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subTask(@NotNull String name) {
|
||||
public void subTask(String name) {
|
||||
super.subTask(name);
|
||||
asyncTask.setStatus(name);
|
||||
WebEventUtils.sendAsyncTaskEvent(webSession, asyncTask);
|
||||
|
||||
+3
-33
@@ -17,18 +17,12 @@
|
||||
package io.cloudbeaver.registry;
|
||||
|
||||
import org.eclipse.core.runtime.IConfigurationElement;
|
||||
import org.eclipse.core.runtime.Platform;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.DBPConditionalProperty;
|
||||
import org.jkiss.dbeaver.model.impl.LocalizedPropertyDescriptor;
|
||||
import org.jkiss.dbeaver.utils.RuntimeUtils;
|
||||
import org.osgi.framework.Bundle;
|
||||
|
||||
public class WebAuthProviderProperty extends LocalizedPropertyDescriptor implements DBPConditionalProperty {
|
||||
|
||||
private static final String WEB_AUTH_PROPERTY_PREFIX = "prop.auth.model.";
|
||||
|
||||
private final String[] requiredFeatures;
|
||||
@Nullable
|
||||
private final String type;
|
||||
@@ -36,7 +30,6 @@ public class WebAuthProviderProperty extends LocalizedPropertyDescriptor impleme
|
||||
private final String authProviderId;
|
||||
private final String hideExpr;
|
||||
private final String readOnlyExpr;
|
||||
private final Bundle bundle;
|
||||
|
||||
public WebAuthProviderProperty(String category, IConfigurationElement config, String authProviderId) {
|
||||
super(category, config);
|
||||
@@ -46,7 +39,6 @@ public class WebAuthProviderProperty extends LocalizedPropertyDescriptor impleme
|
||||
this.type = config.getAttribute("type");
|
||||
this.hideExpr = config.getAttribute("hideExpr");
|
||||
this.readOnlyExpr = config.getAttribute("readOnlyExpr");
|
||||
this.bundle = extractBundle(config);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -71,34 +63,12 @@ public class WebAuthProviderProperty extends LocalizedPropertyDescriptor impleme
|
||||
return readOnlyExpr;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getLocalizedName(@NotNull String locale) {
|
||||
return RuntimeUtils.getBundleLocalization(bundle, locale).getString(getPropertyId());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getLocalizedDescription(@NotNull String locale) {
|
||||
return RuntimeUtils.getBundleLocalization(bundle, locale).getString(getPropertyId() + "." + ATTR_DESCRIPTION);
|
||||
}
|
||||
|
||||
private String getPropertyId() {
|
||||
public String getPropertyId() {
|
||||
if (authProviderId != null) {
|
||||
return WEB_AUTH_PROPERTY_PREFIX + authProviderId + "." + this.getId();
|
||||
return "prop.auth.model." + authProviderId + "." + this.getId();
|
||||
} else {
|
||||
return WEB_AUTH_PROPERTY_PREFIX + this.getId();
|
||||
return "prop.auth.model." + this.getId();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Bundle extractBundle(@NotNull IConfigurationElement config) {
|
||||
final Bundle bundle;
|
||||
String bundleName = config.getContributor().getName();
|
||||
bundle = Platform.getBundle(bundleName);
|
||||
if (bundle == null) {
|
||||
throw new IllegalStateException("Bundle '" + bundleName + "' not found");
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
}
|
||||
|
||||
-3
@@ -45,7 +45,4 @@ public class WebSQLConstants {
|
||||
public static final String ATTR_SRID = "srid";
|
||||
public static final String ATTR_PROPERTIES = "properties";
|
||||
|
||||
public static final String TASK_CONFIRMATION_ATTR_PREFIX = "session_task_confirmation_";
|
||||
public static final String SKIP_TASK_CONFIRMATIONS_ATTR = "skip_session_task_confirmations";
|
||||
public static final int TASK_CONFIRMATION_TIMEOUT_SECONDS = 60;
|
||||
}
|
||||
|
||||
-12
@@ -21,7 +21,6 @@ import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.DBPDataSource;
|
||||
import org.jkiss.dbeaver.model.DBUtils;
|
||||
import org.jkiss.dbeaver.model.data.DBDAttributeBinding;
|
||||
import org.jkiss.dbeaver.model.data.DBDDataFilter;
|
||||
import org.jkiss.dbeaver.model.data.DBDRowIdentifier;
|
||||
import org.jkiss.dbeaver.model.exec.trace.DBCTrace;
|
||||
import org.jkiss.dbeaver.model.struct.*;
|
||||
@@ -43,8 +42,6 @@ public class WebSQLResultsInfo {
|
||||
// TODO: find a way to remove isSingleRow and use virtual keys for reading BLOB and string cell values.
|
||||
private boolean isSingleRow;
|
||||
private DBCTrace trace;
|
||||
@Nullable
|
||||
private DBDDataFilter dataFilter;
|
||||
private String queryText;
|
||||
|
||||
public WebSQLResultsInfo(@NotNull DBSDataContainer dataContainer, @NotNull String id) {
|
||||
@@ -154,13 +151,4 @@ public class WebSQLResultsInfo {
|
||||
public void setSingleRow(boolean singleRow) {
|
||||
isSingleRow = singleRow;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DBDDataFilter getDataFilter() {
|
||||
return dataFilter;
|
||||
}
|
||||
|
||||
public void setDataFilter(@Nullable DBDDataFilter dataFilter) {
|
||||
this.dataFilter = dataFilter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
package io.cloudbeaver.utils;
|
||||
|
||||
import org.jkiss.dbeaver.model.DBConstants;
|
||||
import org.jkiss.utils.StandardConstants;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
public class CBModelConstants {
|
||||
public static final DateTimeFormatter ISO_DATE_FORMAT = DateTimeFormatter.ofPattern(DBConstants.DEFAULT_ISO_TIMESTAMP_FORMAT)
|
||||
.withZone(ZoneId.of("UTC"));
|
||||
.withZone(StandardConstants.ZONE_ID_UTC);
|
||||
}
|
||||
|
||||
+18
-14
@@ -16,6 +16,10 @@
|
||||
*/
|
||||
package io.cloudbeaver.utils;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.InstanceCreator;
|
||||
import com.google.gson.Strictness;
|
||||
import io.cloudbeaver.DBWConstants;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebSessionProjectImpl;
|
||||
@@ -30,6 +34,7 @@ import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBConstants;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.access.DBAAuthCredentials;
|
||||
import org.jkiss.dbeaver.model.access.DBAAuthCredentialsWithComplexProperties;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
|
||||
@@ -38,9 +43,7 @@ import org.jkiss.dbeaver.model.connection.DBPDriver;
|
||||
import org.jkiss.dbeaver.model.impl.auth.AuthModelDatabaseNativeCredentials;
|
||||
import org.jkiss.dbeaver.model.net.DBWHandlerConfiguration;
|
||||
import org.jkiss.dbeaver.model.net.ssh.SSHConstants;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceDisconnectEvent;
|
||||
import org.jkiss.dbeaver.registry.DataSourceUtils;
|
||||
import org.jkiss.dbeaver.registry.network.NetworkHandlerDescriptor;
|
||||
import org.jkiss.dbeaver.registry.network.NetworkHandlerRegistry;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
@@ -208,30 +211,31 @@ public class WebDataSourceUtils {
|
||||
return webSession.getAccessibleProjectById(projectId).getWebConnectionInfo(connectionId);
|
||||
}
|
||||
|
||||
public static void updateCredentialsFromProperties(
|
||||
@NotNull DBRProgressMonitor progressMonitor,
|
||||
@NotNull DBAAuthCredentials credentials,
|
||||
@Nullable Map<String, ?> properties
|
||||
) {
|
||||
if (properties == null) {
|
||||
return;
|
||||
|
||||
public static void updateCredentialsFromProperties(@NotNull DBAAuthCredentials credentials, @NotNull Map<String, ?> properties) {
|
||||
InstanceCreator<DBAAuthCredentials> credTypeAdapter = type -> credentials;
|
||||
Gson credGson = new GsonBuilder()
|
||||
.setStrictness(Strictness.LENIENT)
|
||||
.registerTypeAdapter(credentials.getClass(), credTypeAdapter)
|
||||
.create();
|
||||
|
||||
if (credentials instanceof DBAAuthCredentialsWithComplexProperties complexProperties) {
|
||||
complexProperties.updateCredentialsFromComplexProperties(properties);
|
||||
}
|
||||
DataSourceUtils.updateCredentialsFromProperties(progressMonitor, credentials, properties);
|
||||
credGson.fromJson(credGson.toJsonTree(properties), credentials.getClass());
|
||||
}
|
||||
|
||||
public static void saveAuthProperties(
|
||||
@NotNull DBRProgressMonitor progressMonitor,
|
||||
@NotNull DBPDataSourceContainer dataSourceContainer,
|
||||
@NotNull DBPConnectionConfiguration configuration,
|
||||
@Nullable Map<String, Object> authProperties,
|
||||
boolean saveCredentials,
|
||||
boolean sharedCredentials
|
||||
) {
|
||||
saveAuthProperties(progressMonitor, dataSourceContainer, configuration, authProperties, saveCredentials, sharedCredentials, false);
|
||||
saveAuthProperties(dataSourceContainer, configuration, authProperties, saveCredentials, sharedCredentials, false);
|
||||
}
|
||||
|
||||
public static void saveAuthProperties(
|
||||
@NotNull DBRProgressMonitor progressMonitor,
|
||||
@NotNull DBPDataSourceContainer dataSourceContainer,
|
||||
@NotNull DBPConnectionConfiguration configuration,
|
||||
@Nullable Map<String, Object> authProperties,
|
||||
@@ -267,7 +271,7 @@ public class WebDataSourceUtils {
|
||||
configuration.setAuthProperties(currentAuthProps);
|
||||
}
|
||||
if (!authProperties.isEmpty()) {
|
||||
updateCredentialsFromProperties(progressMonitor, credentials, authProperties);
|
||||
updateCredentialsFromProperties(credentials, authProperties);
|
||||
}
|
||||
|
||||
configuration.getAuthModel().saveCredentials(dataSourceContainer, configuration, credentials);
|
||||
|
||||
@@ -18,7 +18,6 @@ package io.cloudbeaver.utils;
|
||||
|
||||
import io.cloudbeaver.model.WebAsyncTaskInfo;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.websocket.event.task.WSSessionTaskInfoEvent;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
@@ -31,6 +30,7 @@ import org.jkiss.dbeaver.model.websocket.event.datasource.WSDatasourceFolderEven
|
||||
import org.jkiss.dbeaver.model.websocket.event.permissions.WSSubjectPermissionEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.resource.WSResourceProperty;
|
||||
import org.jkiss.dbeaver.model.websocket.event.resource.WSResourceUpdatedEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.session.WSSessionTaskInfoEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@@ -17,11 +17,8 @@
|
||||
|
||||
package io.cloudbeaver.utils;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
@@ -42,30 +39,4 @@ public class WebTestUtils {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void setPrivateField(
|
||||
@NotNull Object target,
|
||||
@NotNull String fieldName,
|
||||
@Nullable Object value
|
||||
) throws Exception {
|
||||
Class<?> cls = target.getClass();
|
||||
Field f = null;
|
||||
while (cls != null) {
|
||||
Field[] fields = cls.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
if (field.getName().equals(fieldName)) {
|
||||
f = field;
|
||||
break;
|
||||
}
|
||||
}
|
||||
cls = cls.getSuperclass();
|
||||
}
|
||||
if (f == null) {
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
}
|
||||
boolean isAccessible = f.canAccess(target);
|
||||
f.setAccessible(true);
|
||||
f.set(target, value);
|
||||
f.setAccessible(isAccessible);
|
||||
}
|
||||
}
|
||||
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.client;
|
||||
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSClientEvent;
|
||||
|
||||
public class WSSessionPingClientEvent extends WSClientEvent {
|
||||
public static final String ID = "cb_client_session_ping";
|
||||
|
||||
public WSSessionPingClientEvent(@Nullable String topicId) {
|
||||
super(ID, topicId);
|
||||
}
|
||||
}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.client;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.websocket.WSConstants;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSClientEvent;
|
||||
|
||||
public class WSSessionTaskConfirmationEvent extends WSClientEvent {
|
||||
public static final String ID = "cb_client_session_task_confirmation";
|
||||
|
||||
private final String taskId;
|
||||
private final boolean confirmed;
|
||||
private final boolean skipConfirmations;
|
||||
|
||||
public WSSessionTaskConfirmationEvent(
|
||||
@NotNull String taskId,
|
||||
boolean confirmed,
|
||||
boolean skipConfirmations
|
||||
) {
|
||||
super(ID, WSConstants.TOPIC_SESSION_TASK);
|
||||
this.taskId = taskId;
|
||||
this.confirmed = confirmed;
|
||||
this.skipConfirmations = skipConfirmations;
|
||||
}
|
||||
|
||||
public String getTaskId() {
|
||||
return taskId;
|
||||
}
|
||||
|
||||
public boolean isConfirmed() {
|
||||
return confirmed;
|
||||
}
|
||||
|
||||
public boolean isSkipConfirmations() {
|
||||
return skipConfirmations;
|
||||
}
|
||||
}
|
||||
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.client;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.websocket.WSConstants;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSClientEvent;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class WSSessionTaskWithParametersConfirmationEvent extends WSClientEvent {
|
||||
public static final String ID = "cb_client_session_task_with_parameters_confirmation";
|
||||
@NotNull
|
||||
private final String taskId;
|
||||
@NotNull
|
||||
private final Map<String, Object> parameters;
|
||||
|
||||
protected WSSessionTaskWithParametersConfirmationEvent(@NotNull String taskId, @NotNull Map<String, Object> parameters) {
|
||||
super(ID, WSConstants.TOPIC_SESSION_TASK);
|
||||
this.taskId = taskId;
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getTaskId() {
|
||||
return taskId;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<String, Object> getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.client;
|
||||
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSClientEvent;
|
||||
|
||||
/**
|
||||
* Subscribe on event topic
|
||||
*/
|
||||
public class WSSubscribeOnTopicClientEvent extends WSClientEvent {
|
||||
public static final String ID = "cb_client_topic_subscribe";
|
||||
|
||||
public WSSubscribeOnTopicClientEvent(String topicId) {
|
||||
super(ID, topicId);
|
||||
}
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.client;
|
||||
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSClientEvent;
|
||||
|
||||
/**
|
||||
* Unsubscribe from event topic
|
||||
*/
|
||||
public class WSUnsubscribeFromTopicClientEvent extends WSClientEvent {
|
||||
public static final String ID = "cb_client_topic_unsubscribe";
|
||||
|
||||
public WSUnsubscribeFromTopicClientEvent(String topicId) {
|
||||
super(ID, topicId);
|
||||
}
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.client;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.websocket.WSConstants;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSClientEvent;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Subscribe on event topic
|
||||
*/
|
||||
public class WSUpdateActiveProjectsClientEvent extends WSClientEvent {
|
||||
public static final String ID = "cb_client_projects_active";
|
||||
|
||||
@NotNull
|
||||
private final Set<String> projectIds;
|
||||
|
||||
public WSUpdateActiveProjectsClientEvent(@NotNull Set<String> projectIds) {
|
||||
super(ID, WSConstants.TOPIC_PROJECTS);
|
||||
this.projectIds = projectIds;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Set<String> getProjectIds() {
|
||||
return projectIds;
|
||||
}
|
||||
}
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.task;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.websocket.WSConstants;
|
||||
import org.jkiss.dbeaver.model.websocket.event.session.WSAbstractSessionEvent;
|
||||
|
||||
public abstract class WSAbstractSessionTaskEvent extends WSAbstractSessionEvent {
|
||||
@NotNull
|
||||
private final String taskId;
|
||||
@NotNull
|
||||
private final String title;
|
||||
@NotNull
|
||||
private final String message;
|
||||
|
||||
public WSAbstractSessionTaskEvent(
|
||||
@NotNull String eventId,
|
||||
@NotNull String taskId,
|
||||
@NotNull String title,
|
||||
@NotNull String message
|
||||
) {
|
||||
super(eventId, WSConstants.TOPIC_SESSION_TASK);
|
||||
this.taskId = taskId;
|
||||
this.title = title;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getTaskId() {
|
||||
return taskId;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.task;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
|
||||
public class WSSessionTaskConfirmationRequestEvent extends WSAbstractSessionTaskEvent {
|
||||
private static final String ID = "cb_session_task_confirmation_request";
|
||||
|
||||
public WSSessionTaskConfirmationRequestEvent(
|
||||
@NotNull String taskId,
|
||||
@NotNull String title,
|
||||
@NotNull String message
|
||||
) {
|
||||
super(ID, taskId, title, message);
|
||||
}
|
||||
}
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.task;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.websocket.WSConstants;
|
||||
import org.jkiss.dbeaver.model.websocket.event.session.WSAbstractSessionEvent;
|
||||
|
||||
/**
|
||||
* Async task info event.
|
||||
*/
|
||||
public class WSSessionTaskInfoEvent extends WSAbstractSessionEvent {
|
||||
@NotNull
|
||||
private final String taskId;
|
||||
@Nullable
|
||||
private final String statusName;
|
||||
private final boolean running;
|
||||
|
||||
public WSSessionTaskInfoEvent(
|
||||
@NotNull String taskId,
|
||||
@Nullable String statusName,
|
||||
boolean running
|
||||
) {
|
||||
super("cb_session_task_info_updated", WSConstants.TOPIC_SESSION_TASK);
|
||||
this.taskId = taskId;
|
||||
this.statusName = statusName;
|
||||
this.running = running;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getTaskId() {
|
||||
return taskId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getStatusName() {
|
||||
return statusName;
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
}
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.task;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
|
||||
public class WSSessionTaskQueryConfirmationRequestEvent extends WSAbstractSessionTaskEvent {
|
||||
private static final String ID = "cb_session_task_query_confirmation_request";
|
||||
|
||||
@NotNull
|
||||
private final String query;
|
||||
|
||||
public WSSessionTaskQueryConfirmationRequestEvent(
|
||||
@NotNull String taskId,
|
||||
@NotNull String title,
|
||||
@NotNull String message,
|
||||
@NotNull String query
|
||||
) {
|
||||
super(ID, taskId, title, message);
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
}
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.task;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class WSSessionTaskQueryParamsConfirmationEvent extends WSAbstractSessionTaskEvent {
|
||||
private static final String ID = "cb_session_task_query_params_confirmation_request";
|
||||
@NotNull
|
||||
private final String query;
|
||||
@NotNull
|
||||
private final Map<String, Object> parameters;
|
||||
|
||||
public WSSessionTaskQueryParamsConfirmationEvent(
|
||||
@NotNull String taskId,
|
||||
@NotNull String title,
|
||||
@NotNull String message,
|
||||
@NotNull String query,
|
||||
@NotNull Map<String, Object> parameters
|
||||
) {
|
||||
super(ID, taskId, title, message);
|
||||
this.query = query;
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<String, Object> getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Community Product
|
||||
Bundle-SymbolicName: io.cloudbeaver.product.ce;singleton:=true
|
||||
Bundle-Version: 25.3.1.qualifier
|
||||
Bundle-Release-Date: 20251222
|
||||
Bundle-Version: 25.2.4.qualifier
|
||||
Bundle-Release-Date: 20251103
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: io.cloudbeaver.server.ce
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.product.ce</artifactId>
|
||||
<version>25.3.1-SNAPSHOT</version>
|
||||
<version>25.2.4-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -2,8 +2,8 @@ Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Cloudbeaver Base JDBC drivers
|
||||
Bundle-SymbolicName: io.cloudbeaver.resources.drivers.base;singleton:=true
|
||||
Bundle-Version: 1.0.136.qualifier
|
||||
Bundle-Release-Date: 20251222
|
||||
Bundle-Version: 1.0.133.qualifier
|
||||
Bundle-Release-Date: 20251103
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Automatic-Module-Name: io.cloudbeaver.resources.drivers.base
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.resources.drivers.base</artifactId>
|
||||
<version>1.0.136-SNAPSHOT</version>
|
||||
<version>1.0.133-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
</project>
|
||||
|
||||
@@ -3,13 +3,12 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver CE Server
|
||||
Bundle-SymbolicName: io.cloudbeaver.server.ce;singleton:=true
|
||||
Bundle-Version: 25.3.1.qualifier
|
||||
Bundle-Release-Date: 20251222
|
||||
Bundle-Version: 25.2.4.qualifier
|
||||
Bundle-Release-Date: 20251103
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-Activator: io.cloudbeaver.server.CBPlatformActivator
|
||||
Require-Bundle: io.cloudbeaver.server;visibility:=reexport,
|
||||
io.cloudbeaver.model.cli;visibility:=reexport,
|
||||
com.google.guava
|
||||
Export-Package: io.cloudbeaver,
|
||||
io.cloudbeaver.model,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.server.ce</artifactId>
|
||||
<version>25.3.1-SNAPSHOT</version>
|
||||
<version>25.2.4-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
-2
@@ -86,12 +86,10 @@ public class CBServerConfig implements WebServerConfiguration {
|
||||
return contentRoot;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getRootURI() {
|
||||
return rootURI;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getServicesURI() {
|
||||
return serviceURI;
|
||||
}
|
||||
|
||||
+5
-24
@@ -24,7 +24,6 @@ import io.cloudbeaver.model.app.BaseServletApplication;
|
||||
import io.cloudbeaver.model.app.ServletAuthApplication;
|
||||
import io.cloudbeaver.model.app.ServletAuthConfiguration;
|
||||
import io.cloudbeaver.model.app.ServletSystemInformationCollector;
|
||||
import io.cloudbeaver.model.cli.CloudBeaverInstanceServer;
|
||||
import io.cloudbeaver.model.config.CBAppConfig;
|
||||
import io.cloudbeaver.model.config.CBServerConfig;
|
||||
import io.cloudbeaver.registry.WebDriverRegistry;
|
||||
@@ -87,7 +86,6 @@ public abstract class CBApplication<T extends CBServerConfig>
|
||||
*/
|
||||
private static final long CONFIGURATION_MODE_SESSION_IDLE_TIME = 60 * 60 * 1000 * 24 * 7;
|
||||
|
||||
private CloudBeaverInstanceServer instanceServer;
|
||||
|
||||
static {
|
||||
Log.setDefaultDebugStream(System.out);
|
||||
@@ -121,7 +119,6 @@ public abstract class CBApplication<T extends CBServerConfig>
|
||||
this.homeDirectory = new File(initHomeFolder());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getServerURL() {
|
||||
return getServerConfiguration().getServerURL();
|
||||
}
|
||||
@@ -141,18 +138,15 @@ public abstract class CBApplication<T extends CBServerConfig>
|
||||
return getServerConfiguration().getServerName();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getRootURI() {
|
||||
return getServerConfiguration().getRootURI();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getServicesURI() {
|
||||
return getServerConfiguration().getServicesURI();
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public Path getHomeDirectory() {
|
||||
return homeDirectory.toPath();
|
||||
}
|
||||
@@ -176,12 +170,10 @@ public abstract class CBApplication<T extends CBServerConfig>
|
||||
* @return max session idle time from server configuration, may differ from {@link #getMaxSessionIdleTime()}
|
||||
*/
|
||||
|
||||
@NotNull
|
||||
public CBAppConfig getAppConfiguration() {
|
||||
return getServerConfigurationController().getAppConfiguration();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public T getServerConfiguration() {
|
||||
return getServerConfigurationController().getServerConfiguration();
|
||||
}
|
||||
@@ -207,11 +199,6 @@ public abstract class CBApplication<T extends CBServerConfig>
|
||||
|
||||
@Override
|
||||
protected void startServer() {
|
||||
try {
|
||||
this.instanceServer = createInstanceServer();
|
||||
} catch (Exception e) {
|
||||
log.error("Error initializing instance server", e);
|
||||
}
|
||||
try {
|
||||
if (!loadServerConfiguration()) {
|
||||
return;
|
||||
@@ -240,7 +227,10 @@ public abstract class CBApplication<T extends CBServerConfig>
|
||||
Location instanceLoc = Platform.getInstanceLocation();
|
||||
try {
|
||||
if (!instanceLoc.isSet()) { // always false?
|
||||
URL wsLocationURL = getWorkspaceDirectory().toUri().toURL();
|
||||
URL wsLocationURL = new URL(
|
||||
"file", //$NON-NLS-1$
|
||||
null,
|
||||
getWorkspaceDirectory().toAbsolutePath().toString());
|
||||
instanceLoc.set(wsLocationURL, true);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -351,7 +341,6 @@ public abstract class CBApplication<T extends CBServerConfig>
|
||||
getAppConfiguration().setEnabledFeatures(enabledFeatures.toArray(new String[0]));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected ServletSystemInformationCollector<?> createSystemInformationCollector() {
|
||||
return new ServletSystemInformationCollector<>(this);
|
||||
}
|
||||
@@ -530,7 +519,6 @@ public abstract class CBApplication<T extends CBServerConfig>
|
||||
return getServerConfigurationController().getLocalHostAddress();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<InetAddress> getLocalInetAddresses() {
|
||||
return localInetAddresses;
|
||||
}
|
||||
@@ -679,7 +667,6 @@ public abstract class CBApplication<T extends CBServerConfig>
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CBSessionManager getSessionManager() {
|
||||
if (sessionManager == null) {
|
||||
sessionManager = createSessionManager();
|
||||
@@ -704,7 +691,6 @@ public abstract class CBApplication<T extends CBServerConfig>
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public WSEventController getEventController() {
|
||||
return eventController;
|
||||
@@ -786,18 +772,17 @@ public abstract class CBApplication<T extends CBServerConfig>
|
||||
initActions.remove(actionId);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<String, String> getInitActions() {
|
||||
return Map.copyOf(initActions);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public WebServerConfig getWebServerConfig() {
|
||||
return new CBWebServerConfig(this);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ServletSystemInformationCollector<?> getSystemInformationCollector() {
|
||||
return systemInformationCollector;
|
||||
}
|
||||
@@ -810,8 +795,4 @@ public abstract class CBApplication<T extends CBServerConfig>
|
||||
public <T> T getApplicationContextValue(@NotNull String key) {
|
||||
return (T) applicationContext.get(key);
|
||||
}
|
||||
|
||||
protected CloudBeaverInstanceServer createInstanceServer() throws IOException {
|
||||
return new CloudBeaverInstanceServer();
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -17,6 +17,8 @@
|
||||
package io.cloudbeaver.server;
|
||||
|
||||
import io.cloudbeaver.auth.NoAuthCredentialsProvider;
|
||||
import io.cloudbeaver.model.CBWebServerConfig;
|
||||
import io.cloudbeaver.model.WebServerConfig;
|
||||
import io.cloudbeaver.model.config.CBServerConfig;
|
||||
import io.cloudbeaver.model.rm.local.LocalResourceController;
|
||||
import io.cloudbeaver.service.security.CBEmbeddedSecurityController;
|
||||
@@ -77,7 +79,6 @@ public class CBApplicationCE extends CBApplication<CBServerConfig> {
|
||||
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public RMController createResourceController(@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@NotNull DBPWorkspace workspace) throws DBException {
|
||||
|
||||
@@ -74,9 +74,8 @@ public class CBPlatform extends BaseWebPlatform {
|
||||
.scheduleMonitor();
|
||||
|
||||
new AbstractJob("Delete temp folder") {
|
||||
@NotNull
|
||||
@Override
|
||||
protected IStatus run(@NotNull DBRProgressMonitor monitor) {
|
||||
protected IStatus run(DBRProgressMonitor monitor) {
|
||||
try {
|
||||
IOUtils.deleteDirectory(getTempFolder(monitor, TEMP_FILE_FOLDER));
|
||||
IOUtils.deleteDirectory(getTempFolder(monitor, TEMP_FILE_IMPORT_FOLDER));
|
||||
|
||||
+4
-14
@@ -26,7 +26,6 @@ import io.cloudbeaver.server.graphql.GraphQLEndpoint;
|
||||
import io.cloudbeaver.server.servlets.CBImageServlet;
|
||||
import io.cloudbeaver.server.servlets.CBStaticServlet;
|
||||
import io.cloudbeaver.server.servlets.WebStatusServlet;
|
||||
import io.cloudbeaver.server.websockets.CBEventsLongPollingServlet;
|
||||
import io.cloudbeaver.server.websockets.CBEventsWebSocket;
|
||||
import io.cloudbeaver.server.websockets.CBWebSocketServerConfigurator;
|
||||
import io.cloudbeaver.service.DBWServiceBindingServlet;
|
||||
@@ -48,13 +47,11 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class CBJettyServer {
|
||||
|
||||
private static final Log log = Log.getLog(CBJettyServer.class);
|
||||
|
||||
static {
|
||||
// Set Jetty log level to WARN
|
||||
System.setProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.StdErrLog");
|
||||
@@ -121,9 +118,6 @@ public class CBJettyServer {
|
||||
|
||||
servletContextHandler.addServlet(new ServletHolder("status", new WebStatusServlet()), "/status");
|
||||
|
||||
ServletHolder eventsServletHolder = new ServletHolder("events", new CBEventsLongPollingServlet());
|
||||
servletContextHandler.addServlet(eventsServletHolder, serverConfiguration.getServicesURI() + "events/*");
|
||||
|
||||
GraphQLEndpoint endpoint = new GraphQLEndpoint(new ServerConfigurationTimeLimitFilter(application));
|
||||
application.addApplicationContextValue(GraphQL.class.getName(), endpoint.getGraphQL());
|
||||
String gqlServletPath = serverConfiguration.getServicesURI() + "gql/*";
|
||||
@@ -201,15 +195,11 @@ public class CBJettyServer {
|
||||
log.debug("\t" + sm.getServletName() + ": " + Arrays.toString(sm.getPathSpecs())); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
List<String> wsMappings = webSocketContext.getMappings();
|
||||
if (!wsMappings.isEmpty()) {
|
||||
log.debug("Active websocket mappings:");
|
||||
for (String mapping : wsMappings) {
|
||||
log.debug("\t" + mapping);
|
||||
}
|
||||
} else {
|
||||
log.debug("No websocket mappings");
|
||||
log.debug("Active websocket mappings:");
|
||||
for (String mapping : webSocketContext.getMappings()) {
|
||||
log.debug("\t" + mapping);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
boolean forwardProxy = application.getAppConfiguration().isEnabledForwardProxy();
|
||||
|
||||
+4
-4
@@ -49,10 +49,10 @@ public class CBSessionManager implements WebAppSessionManager {
|
||||
|
||||
private static final Log log = Log.getLog(CBSessionManager.class);
|
||||
|
||||
private final CBApplication<?> application;
|
||||
private final CBApplication application;
|
||||
private final Map<String, BaseWebSession> sessionMap = new HashMap<>();
|
||||
|
||||
public CBSessionManager(CBApplication<?> application) {
|
||||
public CBSessionManager(CBApplication application) {
|
||||
this.application = application;
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ public class CBSessionManager implements WebAppSessionManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected CBApplication<?> getApplication() {
|
||||
protected CBApplication getApplication() {
|
||||
return application;
|
||||
}
|
||||
|
||||
@@ -264,7 +264,7 @@ public class CBSessionManager implements WebAppSessionManager {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected Map<String, DBWSessionHandler<WebSession>> getSessionHandlers() {
|
||||
protected Map<String, DBWSessionHandler> getSessionHandlers() {
|
||||
return WebHandlerRegistry.getInstance().getSessionHandlers()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(WebSessionHandlerDescriptor::getId, WebSessionHandlerDescriptor::getInstance));
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Server
|
||||
Bundle-SymbolicName: io.cloudbeaver.server;singleton:=true
|
||||
Bundle-Version: 25.3.1.qualifier
|
||||
Bundle-Release-Date: 20251222
|
||||
Bundle-Version: 25.2.4.qualifier
|
||||
Bundle-Release-Date: 20251103
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: org.eclipse.core.runtime;visibility:=reexport,
|
||||
@@ -18,7 +18,6 @@ Require-Bundle: org.eclipse.core.runtime;visibility:=reexport,
|
||||
org.jkiss.bundle.apache.dbcp,
|
||||
org.jkiss.dbeaver.net.ssh,
|
||||
io.cloudbeaver.model;visibility:=reexport,
|
||||
io.cloudbeaver.model.cli,
|
||||
io.cloudbeaver.service.security;visibility:=reexport
|
||||
Export-Package: io.cloudbeaver,
|
||||
io.cloudbeaver.model,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.server</artifactId>
|
||||
<version>25.3.1-SNAPSHOT</version>
|
||||
<version>25.2.4-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -69,11 +69,6 @@ enum CBServerEventId {
|
||||
cb_workspace_config_changed @since(version: "25.1.1"),
|
||||
"Server notification"
|
||||
cb_server_notification@since(version: "25.2.1")
|
||||
|
||||
"Session task confirmations"
|
||||
cb_session_task_confirmation_request @since(version: "25.2.2")
|
||||
cb_session_task_query_confirmation_request @since(version: "25.2.2")
|
||||
cb_session_task_query_params_confirmation_request @since(version: "25.2.5")
|
||||
}
|
||||
|
||||
"Events sent by client"
|
||||
@@ -81,9 +76,7 @@ enum CBClientEventId {
|
||||
cb_client_topic_subscribe,
|
||||
cb_client_topic_unsubscribe,
|
||||
cb_client_projects_active,
|
||||
cb_client_session_ping,
|
||||
cb_client_session_task_confirmation @since(version: "25.2.2")
|
||||
cb_client_session_task_with_parameters_confirmation @since(version: "25.2.5")
|
||||
cb_client_session_ping
|
||||
}
|
||||
|
||||
"Client subscribes on topic to receive only related events"
|
||||
@@ -301,48 +294,6 @@ type WSServerNotificationEvent implements CBServerEvent @since(version: "25.2.1"
|
||||
notificationType: WSServerNotificationEventType!
|
||||
}
|
||||
|
||||
type WSSessionTaskConfirmationRequestEvent implements CBServerEvent @since(version: "25.2.2") {
|
||||
id: CBServerEventId!
|
||||
topicId: CBEventTopic!
|
||||
taskId: ID!
|
||||
title: String!
|
||||
message: String!
|
||||
}
|
||||
|
||||
type WSSessionTaskQueryConfirmationRequestEvent implements CBServerEvent @since(version: "25.2.2") {
|
||||
id: CBServerEventId!
|
||||
topicId: CBEventTopic!
|
||||
taskId: ID!
|
||||
title: String!
|
||||
message: String!
|
||||
query: String!
|
||||
}
|
||||
|
||||
type WSSessionTaskConfirmationEvent implements CBClientEvent @since(version: "25.2.2") {
|
||||
id: CBClientEventId!
|
||||
topicId: CBEventTopic
|
||||
taskId: ID!
|
||||
confirmed: Boolean!
|
||||
skipConfirmations: Boolean!
|
||||
}
|
||||
|
||||
type WSSessionTaskQueryParamsConfirmationEvent implements CBServerEvent @since(version: "25.2.5") {
|
||||
id: CBServerEventId!
|
||||
topicId: CBEventTopic!
|
||||
taskId: ID!
|
||||
title: String!
|
||||
message: String!
|
||||
query: String!
|
||||
parameters: Object!
|
||||
}
|
||||
|
||||
type WSSessionTaskWithParametersConfirmationEvent implements CBClientEvent @since(version: "25.2.5") {
|
||||
id: CBClientEventId!
|
||||
topicId: CBEventTopic
|
||||
taskId: ID!
|
||||
parameters: Object!
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
emptyEvent: Boolean
|
||||
}
|
||||
|
||||
@@ -158,6 +158,8 @@ type SQLExecuteInfo {
|
||||
duration: Int!
|
||||
# Actual conditions applied to query
|
||||
filterText: String
|
||||
# # original sql query without SQLDataFilter
|
||||
# originalQuery: String
|
||||
# Full query that was executed, contains all used filters
|
||||
fullQuery: String
|
||||
# Results
|
||||
@@ -360,7 +362,6 @@ extend type Mutation {
|
||||
filter: SQLDataFilter,
|
||||
dataFormat: ResultDataFormat, # requested data format. May be ignored by server
|
||||
readLogs: Boolean # added 23.2.1
|
||||
isInteractive: Boolean # Support WS events requested by server, e.g. confirmations. Added in 23.2.3
|
||||
): AsyncTaskInfo!
|
||||
|
||||
"Creates async task for reading data from the container node"
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.cloudbeaver;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marks a field or method as sensitive, so its value won't be logged.
|
||||
*/
|
||||
@Target(value = {ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface WebParameterSecure {
|
||||
}
|
||||
+2
-3
@@ -17,7 +17,6 @@
|
||||
|
||||
package io.cloudbeaver.registry;
|
||||
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.service.DBWSessionHandler;
|
||||
import org.eclipse.core.runtime.IConfigurationElement;
|
||||
import org.jkiss.code.NotNull;
|
||||
@@ -32,7 +31,7 @@ public class WebSessionHandlerDescriptor extends AbstractContextDescriptor {
|
||||
public static final String EXTENSION_ID = "io.cloudbeaver.servlet.handler"; //$NON-NLS-1$
|
||||
|
||||
private final String id;
|
||||
private final DBWSessionHandler<WebSession> instance;
|
||||
private final DBWSessionHandler instance;
|
||||
|
||||
public WebSessionHandlerDescriptor(IConfigurationElement config) throws DBException {
|
||||
super(config);
|
||||
@@ -46,7 +45,7 @@ public class WebSessionHandlerDescriptor extends AbstractContextDescriptor {
|
||||
return id;
|
||||
}
|
||||
|
||||
public DBWSessionHandler<WebSession> getInstance() {
|
||||
public DBWSessionHandler getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,32 +31,25 @@ import java.util.Map;
|
||||
* Base interface for applications with web ui
|
||||
*/
|
||||
public interface WebApplication extends ServletApplication {
|
||||
@NotNull
|
||||
WebServerConfiguration getServerConfiguration();
|
||||
|
||||
@NotNull
|
||||
WebAppSessionManager getSessionManager();
|
||||
|
||||
@NotNull
|
||||
WebDriverRegistry getDriverRegistry();
|
||||
|
||||
@NotNull
|
||||
WebAppConfiguration getAppConfiguration();
|
||||
|
||||
@NotNull
|
||||
Map<String, Object> getProductConfiguration();
|
||||
|
||||
@NotNull
|
||||
List<InetAddress> getLocalInetAddresses();
|
||||
|
||||
@NotNull
|
||||
Map<String, String> getInitActions();
|
||||
|
||||
boolean isLicenseValid();
|
||||
|
||||
String getLicenseStatus();
|
||||
|
||||
@NotNull
|
||||
WebServerConfig getWebServerConfig();
|
||||
|
||||
}
|
||||
|
||||
+7
-2
@@ -44,7 +44,6 @@ import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.utils.MimeTypes;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
import org.jkiss.utils.HttpConstants;
|
||||
import org.jkiss.utils.IOUtils;
|
||||
@@ -212,7 +211,6 @@ public class GraphQLEndpoint extends HttpServlet {
|
||||
if (path.contentEquals("/schema.json") && develMode) {
|
||||
executeQuery(request, response, GraphQLConstants.SCHEMA_READ_QUERY, null, null);
|
||||
} else if (path.contentEquals("/console") && develMode) {
|
||||
response.setContentType(MimeTypes.TEXT_HTML);
|
||||
try (InputStream consolePageStream = WebServiceUtils.openStaticResource("static/graphiql/index.html")) {
|
||||
IOUtils.copyStream(consolePageStream, response.getOutputStream());
|
||||
}
|
||||
@@ -248,7 +246,14 @@ public class GraphQLEndpoint extends HttpServlet {
|
||||
if (operationName != null) {
|
||||
contextBuilder.operationName(operationName);
|
||||
}
|
||||
String sessionId = GraphQLLoggerUtil.getSmSessionId(request);
|
||||
String userId = GraphQLLoggerUtil.getUserId(request);
|
||||
String loggerMessage = GraphQLLoggerUtil.buildLoggerMessage(sessionId, userId, variables);
|
||||
if (operationName != null) {
|
||||
log.debug("API > " + operationName + loggerMessage);
|
||||
} else if (DEBUG) {
|
||||
log.debug("API > " + query + loggerMessage);
|
||||
}
|
||||
LocalDateTime startTime = LocalDateTime.now();
|
||||
ExecutionInput executionInput = contextBuilder.build();
|
||||
ExecutionResult executionResult = null;
|
||||
|
||||
+34
-78
@@ -16,48 +16,35 @@
|
||||
*/
|
||||
package io.cloudbeaver.server.graphql;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import io.cloudbeaver.WebParameterSecure;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.server.WebAppUtils;
|
||||
import io.cloudbeaver.server.WebApplication;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.qm.QMConstants;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class GraphQLLoggerUtil {
|
||||
|
||||
public static final String LOG_API_GRAPHQL_DEBUG_PARAMETER = "log.api.graphql.debug";
|
||||
public static final Gson GSON = new GsonBuilder().create();
|
||||
public static final String MASK_STRING = "****";
|
||||
private static final Set<String> PROHIBITED_VARIABLES =
|
||||
Set.of("password", "config", "parameters", "settings", "licenseText", "credentials", "username");
|
||||
|
||||
@Nullable
|
||||
public static String getUserId(@NotNull HttpServletRequest request) {
|
||||
public static String getUserId(HttpServletRequest request) {
|
||||
WebSession session = getWebSession(request);
|
||||
if (session == null) {
|
||||
return null;
|
||||
}
|
||||
String userId = session.getUserContext().getUserId();
|
||||
if (userId == null) {
|
||||
if (session.getUserContext().isAuthorizedInSecurityManager()) {
|
||||
return QMConstants.QM_ANONYMOUS_DOMAIN;
|
||||
}
|
||||
return QMConstants.QM_UNAUTHORIZED_DOMAIN;
|
||||
if (userId == null && session.getUserContext().isAuthorizedInSecurityManager()) {
|
||||
return "anonymous";
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getSmSessionId(@NotNull HttpServletRequest request) {
|
||||
public static String getSmSessionId(HttpServletRequest request) {
|
||||
WebSession session = getWebSession(request);
|
||||
if (session == null) {
|
||||
return null;
|
||||
@@ -66,7 +53,7 @@ public class GraphQLLoggerUtil {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static WebSession getWebSession(@NotNull HttpServletRequest request) {
|
||||
public static WebSession getWebSession(HttpServletRequest request) {
|
||||
if (request.getSession() == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -76,74 +63,43 @@ public class GraphQLLoggerUtil {
|
||||
.findWebSession(request);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String buildLoggerMessage(
|
||||
@NotNull String sessionId,
|
||||
@NotNull String userId,
|
||||
@NotNull Method method,
|
||||
@NotNull Object[] args
|
||||
) {
|
||||
StringBuilder sb = new StringBuilder(64)
|
||||
.append(" [").append(userId)
|
||||
.append(", session: ").append(sessionId)
|
||||
.append("]");
|
||||
public static String buildLoggerMessage(String sessionId, String userId, Map<String, Object> variables) {
|
||||
StringBuilder loggerMessage = new StringBuilder(" [user: ").append(userId)
|
||||
.append(", sessionId: ").append(sessionId).append("]");
|
||||
|
||||
if (WebAppUtils.getWebPlatform().getPreferenceStore().getBoolean(LOG_API_GRAPHQL_DEBUG_PARAMETER)) {
|
||||
sb.append('(');
|
||||
String text = maskArgsToString(method, args);
|
||||
if (CommonUtils.isNotEmpty(text)) {
|
||||
sb.append(text);
|
||||
if (WebAppUtils.getWebPlatform().getPreferenceStore().getBoolean(LOG_API_GRAPHQL_DEBUG_PARAMETER)
|
||||
&& variables != null
|
||||
) {
|
||||
loggerMessage.append(" [variables] ");
|
||||
String parsedVariables = parseVarialbes(variables);
|
||||
if (CommonUtils.isNotEmpty(parsedVariables)) {
|
||||
loggerMessage.append(parseVarialbes(variables));
|
||||
}
|
||||
sb.append(')');
|
||||
}
|
||||
return sb.toString();
|
||||
return loggerMessage.toString();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String maskArgsToString(@NotNull Method method, @Nullable Object[] args) {
|
||||
Parameter[] params = method.getParameters();
|
||||
if (params.length == 0 || args == null || args.length == 0) {
|
||||
return "";
|
||||
}
|
||||
private static String parseVarialbes(Map<String, Object> map) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
int limit = Math.min(args.length, params.length);
|
||||
StringJoiner joiner = new StringJoiner(", ");
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
|
||||
for (int i = 0; i < limit; i++) {
|
||||
Object value = args[i];
|
||||
if (value instanceof WebSession || value instanceof ServletRequest || value instanceof ServletResponse) {
|
||||
//we already log sessionId
|
||||
continue;
|
||||
}
|
||||
if (params[i].isAnnotationPresent(WebParameterSecure.class)) {
|
||||
joiner.add(MASK_STRING);
|
||||
continue;
|
||||
}
|
||||
if (value instanceof String sv && CommonUtils.isEmpty(sv)) {
|
||||
boolean isProhibited = PROHIBITED_VARIABLES.stream()
|
||||
.anyMatch(prohibitedKey -> key.toLowerCase().contains(prohibitedKey.toLowerCase()));
|
||||
|
||||
if (isProhibited) {
|
||||
result.append(key).append(": ").append("******** ");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value != null && !isSimple(value.getClass())) {
|
||||
String stringValue;
|
||||
try {
|
||||
stringValue = GSON.toJson(value);
|
||||
} catch (Exception e) {
|
||||
stringValue = value.toString();
|
||||
}
|
||||
joiner.add(stringValue);
|
||||
if (value instanceof Map) {
|
||||
result.append(parseVarialbes((Map<String, Object>) value));
|
||||
} else {
|
||||
joiner.add(String.valueOf(value));
|
||||
result.append(key).append(": ").append(value).append(" ");
|
||||
}
|
||||
}
|
||||
return joiner.toString();
|
||||
return result.toString().trim();
|
||||
}
|
||||
|
||||
private static boolean isSimple(Class<?> cls) {
|
||||
return cls.isPrimitive()
|
||||
|| Number.class.isAssignableFrom(cls)
|
||||
|| CharSequence.class.isAssignableFrom(cls)
|
||||
|| Boolean.class.equals(cls)
|
||||
|| Enum.class.isAssignableFrom(cls);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-2
@@ -66,9 +66,8 @@ public class SqlOutputLogReaderJob extends AbstractJob {
|
||||
this.contextInfoId = contextInfoId;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected IStatus run(@NotNull DBRProgressMonitor monitor) {
|
||||
protected IStatus run(DBRProgressMonitor monitor) {
|
||||
if (!DBWorkbench.getPlatform().isShuttingDown()) {
|
||||
try {
|
||||
if (!dbcStatement.isStatementClosed()) {
|
||||
|
||||
-100
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.server.websockets;
|
||||
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.model.session.BaseWebSession;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.websocket.event.client.*;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.websocket.WSUtils;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSClientEvent;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
public class CBClientEventProcessor {
|
||||
|
||||
private static final Log log = Log.getLog(CBClientEventProcessor.class);
|
||||
|
||||
final BaseWebSession webSession;
|
||||
|
||||
public CBClientEventProcessor(@NotNull BaseWebSession webSession) {
|
||||
this.webSession = webSession;
|
||||
}
|
||||
|
||||
public void process(@Nullable String message) {
|
||||
if (CommonUtils.isEmpty(message)) {
|
||||
return;
|
||||
}
|
||||
WSClientEvent clientEvent;
|
||||
try {
|
||||
clientEvent = WSUtils.clientGson.fromJson(message, WSClientEvent.class);
|
||||
} catch (Exception e) {
|
||||
log.error("Error parsing event: " + e.getMessage(), e);
|
||||
webSession.addSessionError(new DBWebException("Invalid event: " + e.getMessage()));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (clientEvent.getId()) {
|
||||
case WSSubscribeOnTopicClientEvent.ID: {
|
||||
webSession.getEventsFilter().subscribeOnEventTopic(clientEvent.getTopicId());
|
||||
break;
|
||||
}
|
||||
case WSUnsubscribeFromTopicClientEvent.ID: {
|
||||
webSession.getEventsFilter().unsubscribeFromEventTopic(clientEvent.getTopicId());
|
||||
break;
|
||||
}
|
||||
case WSUpdateActiveProjectsClientEvent.ID: {
|
||||
var projectEvent = (WSUpdateActiveProjectsClientEvent) clientEvent;
|
||||
webSession.getEventsFilter().setSubscribedProjects(projectEvent.getProjectIds());
|
||||
break;
|
||||
}
|
||||
case WSSessionPingClientEvent.ID: {
|
||||
if (webSession instanceof WebSession session) {
|
||||
session.updateInfo(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WSSessionTaskConfirmationEvent.ID: {
|
||||
if (webSession instanceof WebSession session) {
|
||||
var taskConfirmationEvent = (WSSessionTaskConfirmationEvent) clientEvent;
|
||||
session.handleTaskConfirmation(
|
||||
taskConfirmationEvent.getTaskId(),
|
||||
taskConfirmationEvent.isConfirmed(),
|
||||
taskConfirmationEvent.isSkipConfirmations()
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WSSessionTaskWithParametersConfirmationEvent.ID: {
|
||||
if (webSession instanceof WebSession session) {
|
||||
var taskParamsConfirmationEvent = (WSSessionTaskWithParametersConfirmationEvent) clientEvent;
|
||||
session.handleTaskConfirmationWithParameters(
|
||||
taskParamsConfirmationEvent.getTaskId(),
|
||||
taskParamsConfirmationEvent.getParameters()
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
var e = new DBWebException("Unknown client event: " + clientEvent.getId());
|
||||
log.error(e.getMessage(), e);
|
||||
webSession.addSessionError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
-103
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.server.websockets;
|
||||
|
||||
import io.cloudbeaver.model.session.BaseWebSession;
|
||||
import io.cloudbeaver.websocket.CBWebSessionEventHandler;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class CBEventsLongPolling implements CBWebSessionEventHandler {
|
||||
|
||||
private static final Log log = Log.getLog(CBEventsLongPolling.class);
|
||||
|
||||
private static final int QUEUE_CAPACITY = 1000;
|
||||
|
||||
private final BaseWebSession webSession;
|
||||
private final BlockingQueue<WSEvent> queue = new LinkedBlockingQueue<>(QUEUE_CAPACITY);
|
||||
private final CBClientEventProcessor processor;
|
||||
private volatile long lastPoll;
|
||||
|
||||
public CBEventsLongPolling(@NotNull BaseWebSession webSession) {
|
||||
this.webSession = webSession;
|
||||
this.lastPoll = System.currentTimeMillis();
|
||||
this.webSession.addEventHandler(this);
|
||||
this.processor = new CBClientEventProcessor(this.webSession);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public BaseWebSession webSession() {
|
||||
return webSession;
|
||||
}
|
||||
|
||||
public long lastPoll() {
|
||||
return lastPoll;
|
||||
}
|
||||
|
||||
public void touch() {
|
||||
lastPoll = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<WSEvent> pollEvents(long timeoutSec) throws InterruptedException {
|
||||
WSEvent first = queue.poll(timeoutSec, TimeUnit.SECONDS);
|
||||
if (first == null) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
List<WSEvent> result = new ArrayList<>();
|
||||
result.add(first);
|
||||
queue.drainTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleWebSessionEvent(@NotNull WSEvent event) {
|
||||
if (!queue.offer(event)) {
|
||||
log.warn("Event queue overflow: sid=" + webSession.getSessionId() +
|
||||
", eventId=" + event.getId());
|
||||
}
|
||||
}
|
||||
|
||||
public void onMessage(@Nullable String message) {
|
||||
processor.process(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
webSession.removeEventHandler(this);
|
||||
queue.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CBEventsLongPolling{" +
|
||||
"sid=" + webSession.getSessionId() +
|
||||
", size=" + queue.size() +
|
||||
", lastPoll=" + lastPoll +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
||||
-261
@@ -1,261 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.server.websockets;
|
||||
|
||||
import io.cloudbeaver.model.session.BaseWebSession;
|
||||
import io.cloudbeaver.model.session.WebHeadlessSession;
|
||||
import io.cloudbeaver.model.session.WebHttpRequestInfo;
|
||||
import io.cloudbeaver.server.CBConstants;
|
||||
import io.cloudbeaver.server.WebAppSessionManager;
|
||||
import io.cloudbeaver.server.WebAppUtils;
|
||||
import io.cloudbeaver.utils.ServletAppUtils;
|
||||
import io.cloudbeaver.websocket.event.client.WSSessionPingClientEvent;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServlet;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.eclipse.jetty.http.BadMessageException;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.websocket.WSConstants;
|
||||
import org.jkiss.dbeaver.model.websocket.WSUtils;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.session.WSSocketConnectedEvent;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
import org.jkiss.utils.HttpConstants;
|
||||
import org.jkiss.utils.rest.RpcConstants;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class CBEventsLongPollingServlet extends HttpServlet {
|
||||
|
||||
private static final Log log = Log.getLog(CBEventsLongPollingServlet.class);
|
||||
|
||||
private static final String PING = WSUtils.clientGson.toJson(new WSSessionPingClientEvent("cb_session"));
|
||||
private static final long POLL_TIMEOUT_SEC = 25;
|
||||
private static final long SESSION_IDLE_TIMEOUT_SEC = 60;
|
||||
|
||||
private volatile boolean running = false;
|
||||
private final Map<String, CBEventsLongPolling> sessions = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public void init() throws ServletException {
|
||||
super.init();
|
||||
running = true;
|
||||
new LongPollingMonitorJob(DBWorkbench.getPlatform(), this).scheduleMonitor();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doOptions(@NotNull HttpServletRequest req, @NotNull HttpServletResponse resp) {
|
||||
addCorsHeaders(req, resp);
|
||||
resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(@NotNull HttpServletRequest req, @NotNull HttpServletResponse resp) throws IOException {
|
||||
addCorsHeaders(req, resp);
|
||||
|
||||
BaseWebSession ws = resolveSession(req);
|
||||
resp.setHeader(WSConstants.WS_SESSION_HEADER, ws.getSessionId());
|
||||
|
||||
CBEventsLongPolling ps = getOrCreatePollSession(ws);
|
||||
ps.onMessage(PING);
|
||||
|
||||
try {
|
||||
List<WSEvent> events = ps.pollEvents(POLL_TIMEOUT_SEC);
|
||||
|
||||
if (events.isEmpty()) {
|
||||
resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
|
||||
return;
|
||||
}
|
||||
|
||||
resp.setContentType(CBConstants.APPLICATION_JSON);
|
||||
WSUtils.clientGson.toJson(Map.of("events", events), resp.getWriter());
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.debug("Long-poll interrupted (likely client disconnected)", e);
|
||||
resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(@NotNull HttpServletRequest req, @NotNull HttpServletResponse resp) throws IOException {
|
||||
addCorsHeaders(req, resp);
|
||||
|
||||
BaseWebSession ws = resolveSession(req);
|
||||
resp.setHeader(WSConstants.WS_SESSION_HEADER, ws.getSessionId());
|
||||
resp.addCookie(new Cookie(CBConstants.CB_SESSION_COOKIE_NAME, ws.getSessionId()));
|
||||
|
||||
CBEventsLongPolling ps = getOrCreatePollSession(ws);
|
||||
|
||||
String json = new String(req.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
|
||||
if (CommonUtils.isEmpty(json)) {
|
||||
sendError(resp, HttpServletResponse.SC_BAD_REQUEST, "Empty payload");
|
||||
return;
|
||||
}
|
||||
|
||||
ps.onMessage(json);
|
||||
|
||||
resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
super.destroy();
|
||||
running = false;
|
||||
sessions.clear();
|
||||
log.debug("HTTP Long-Polling transport stopped");
|
||||
}
|
||||
|
||||
public void cleanupIdleSessions() {
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
sessions.entrySet().removeIf(entry -> {
|
||||
CBEventsLongPolling ps = entry.getValue();
|
||||
long idleMillis = now - ps.lastPoll();
|
||||
if (idleMillis > SESSION_IDLE_TIMEOUT_SEC * 1000L) {
|
||||
try {
|
||||
ps.close();
|
||||
} catch (Exception e) {
|
||||
log.debug("Failed to remove event handler for " + entry.getKey(), e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
private void addCorsHeaders(@NotNull HttpServletRequest req, @NotNull HttpServletResponse resp) {
|
||||
String origin = req.getHeader("Origin");
|
||||
boolean develMode = ServletAppUtils.getServletApplication().getServerConfiguration().isDevelMode();
|
||||
if (!develMode || origin == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
resp.setHeader("Access-Control-Allow-Origin", origin);
|
||||
resp.setHeader("Access-Control-Allow-Credentials", "true");
|
||||
|
||||
String reqHeaders = req.getHeader("Access-Control-Request-Headers");
|
||||
if (reqHeaders != null) {
|
||||
resp.setHeader("Access-Control-Allow-Headers", reqHeaders);
|
||||
}
|
||||
|
||||
resp.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected BaseWebSession resolveSession(@NotNull HttpServletRequest req) {
|
||||
|
||||
String sid = getSessionId(req);
|
||||
|
||||
WebHttpRequestInfo info = new WebHttpRequestInfo(
|
||||
sid,
|
||||
req.getAttribute("locale"),
|
||||
req.getRemoteAddr(),
|
||||
req.getHeader(HttpConstants.HEADER_USER_AGENT)
|
||||
);
|
||||
|
||||
try {
|
||||
|
||||
String token = req.getHeader(WSConstants.WS_AUTH_HEADER);
|
||||
|
||||
WebHeadlessSession headless = getHeadlessSession(token, info);
|
||||
if (headless != null) {
|
||||
return headless;
|
||||
} else {
|
||||
log.trace("Couldn't create headless session");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Error resolving headless session", e);
|
||||
}
|
||||
|
||||
throw new BadMessageException("No web session found for long-poll request");
|
||||
}
|
||||
|
||||
protected WebHeadlessSession getHeadlessSession(String token, WebHttpRequestInfo info) throws DBException {
|
||||
WebAppSessionManager sm = WebAppUtils.getWebApplication().getSessionManager();
|
||||
return sm.getHeadlessSession(token, info, true);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getSessionId(@NotNull HttpServletRequest req) {
|
||||
String sid = req.getHeader(WSConstants.WS_SESSION_HEADER);
|
||||
if (!CommonUtils.isEmpty(sid)) {
|
||||
return sid;
|
||||
}
|
||||
|
||||
if (req.getCookies() != null) {
|
||||
for (var cookie : req.getCookies()) {
|
||||
if (CBConstants.CB_SESSION_COOKIE_NAME.equals(cookie.getName())) {
|
||||
return cookie.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private CBEventsLongPolling getOrCreatePollSession(@NotNull BaseWebSession ws) {
|
||||
final String sid = ws.getSessionId();
|
||||
|
||||
return sessions.compute(sid, (key, existing) -> {
|
||||
if (existing != null) {
|
||||
existing.touch();
|
||||
return existing;
|
||||
}
|
||||
|
||||
CBEventsLongPolling ps = new CBEventsLongPolling(ws);
|
||||
|
||||
ps.handleWebSessionEvent(new WSSocketConnectedEvent(ws.getApplication().getApplicationRunId()));
|
||||
log.debug("HTTP Long-Poll channel opened for session " + sid);
|
||||
|
||||
return ps;
|
||||
});
|
||||
}
|
||||
|
||||
public static void sendError(
|
||||
@NotNull HttpServletResponse resp,
|
||||
int status,
|
||||
@NotNull String message
|
||||
) throws IOException {
|
||||
|
||||
resp.setStatus(status);
|
||||
resp.setContentType(CBConstants.APPLICATION_JSON);
|
||||
|
||||
Map<String, Object> error = Map.of(
|
||||
"error", message,
|
||||
"status", status
|
||||
);
|
||||
|
||||
RpcConstants.COMPACT_GSON.toJson(error, resp.getWriter());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+61
-10
@@ -16,15 +16,22 @@
|
||||
*/
|
||||
package io.cloudbeaver.server.websockets;
|
||||
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.model.session.BaseWebSession;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.websocket.CBWebSessionEventHandler;
|
||||
import jakarta.websocket.*;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSClientEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.client.WSSessionPingClientEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.client.WSSubscribeOnTopicClientEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.client.WSUnsubscribeFromTopicClientEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.client.WSUpdateActiveProjectsClientEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.session.WSAccessTokenExpiredEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.session.WSSocketConnectedEvent;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@@ -48,22 +55,66 @@ public class CBEventsWebSocket extends CBAbstractWebSocket implements CBWebSessi
|
||||
log.debug("EventWebSocket connected to the " + webSession.getSessionId() + " session");
|
||||
|
||||
session.setMaxIdleTimeout(Duration.ofMinutes(5).toMillis());
|
||||
session.addMessageHandler(String.class, new FromUserEventHandler(webSession));
|
||||
session.addMessageHandler(String.class, new FromUserEventHandler());
|
||||
session.addMessageHandler(PongMessage.class, new WebSocketPingPongCallback(webSession));
|
||||
|
||||
CBJettyWebSocketManager.registerWebSocket(webSession.getSessionId(), this);
|
||||
}
|
||||
}
|
||||
|
||||
private static class FromUserEventHandler extends CBClientEventProcessor implements MessageHandler.Whole<String> {
|
||||
|
||||
private FromUserEventHandler(@NotNull BaseWebSession webSession) {
|
||||
super(webSession);
|
||||
}
|
||||
|
||||
private class FromUserEventHandler implements MessageHandler.Whole<String> {
|
||||
@Override
|
||||
public void onMessage(@Nullable String message) {
|
||||
process(message);
|
||||
public void onMessage(String message) {
|
||||
if (CommonUtils.isEmpty(message)) {
|
||||
return;
|
||||
}
|
||||
if (webSession == null) {
|
||||
log.warn("No web session for browser event");
|
||||
return;
|
||||
}
|
||||
WSClientEvent clientEvent;
|
||||
try {
|
||||
clientEvent = CBAbstractWebSocket.gson.fromJson(message, WSClientEvent.class);
|
||||
} catch (Exception e) {
|
||||
if (webSession != null) {
|
||||
webSession.addSessionError(
|
||||
new DBWebException("Invalid websocket event: " + e.getMessage())
|
||||
);
|
||||
}
|
||||
log.error("Error parsing websocket event: " + e.getMessage(), e);
|
||||
return;
|
||||
}
|
||||
if (clientEvent.getId() == null) {
|
||||
webSession.addSessionError(
|
||||
new DBWebException("Invalid websocket event: " + message)
|
||||
);
|
||||
return;
|
||||
}
|
||||
switch (clientEvent.getId()) {
|
||||
case WSSubscribeOnTopicClientEvent.ID: {
|
||||
webSession.getEventsFilter().subscribeOnEventTopic(clientEvent.getTopicId());
|
||||
break;
|
||||
}
|
||||
case WSUnsubscribeFromTopicClientEvent.ID: {
|
||||
webSession.getEventsFilter().unsubscribeFromEventTopic(clientEvent.getTopicId());
|
||||
break;
|
||||
}
|
||||
case WSUpdateActiveProjectsClientEvent.ID: {
|
||||
var projectEvent = (WSUpdateActiveProjectsClientEvent) clientEvent;
|
||||
webSession.getEventsFilter().setSubscribedProjects(projectEvent.getProjectIds());
|
||||
break;
|
||||
}
|
||||
case WSSessionPingClientEvent.ID: {
|
||||
if (webSession instanceof WebSession session) {
|
||||
session.updateInfo(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
var e = new DBWebException("Unknown websocket client event: " + clientEvent.getId());
|
||||
log.error(e.getMessage(), e);
|
||||
webSession.addSessionError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.server.websockets;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.app.DBPPlatform;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.runtime.PeriodicJob;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
public class LongPollingMonitorJob extends PeriodicJob {
|
||||
|
||||
private static final Log log = Log.getLog(LongPollingMonitorJob.class);
|
||||
private static final Duration INTERVAL = Duration.ofSeconds(30);
|
||||
|
||||
private final CBEventsLongPollingServlet longPolling;
|
||||
|
||||
public LongPollingMonitorJob(
|
||||
@NotNull DBPPlatform platform,
|
||||
@NotNull CBEventsLongPollingServlet longPolling
|
||||
) {
|
||||
super("LongPolling monitor", platform, INTERVAL);
|
||||
this.longPolling = longPolling;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doJob(@NotNull DBRProgressMonitor monitor) {
|
||||
if (!longPolling.isRunning()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
longPolling.cleanupIdleSessions();
|
||||
} catch (Throwable t) {
|
||||
log.error("Error during cleanup of long-polling sessions", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-3
@@ -19,7 +19,6 @@ package io.cloudbeaver.server.websockets;
|
||||
import io.cloudbeaver.server.BaseWebPlatform;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.runtime.AbstractJob;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
|
||||
@@ -39,9 +38,8 @@ public class WebSocketPingPongJob extends AbstractJob {
|
||||
setSystem(true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected IStatus run(@NotNull DBRProgressMonitor monitor) {
|
||||
protected IStatus run(DBRProgressMonitor monitor) {
|
||||
if (platform.isShuttingDown()) {
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
|
||||
+8
-22
@@ -16,7 +16,6 @@
|
||||
*/
|
||||
package io.cloudbeaver.service;
|
||||
|
||||
import graphql.GraphQLContext;
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
import graphql.schema.idl.SchemaParser;
|
||||
import graphql.schema.idl.TypeDefinitionRegistry;
|
||||
@@ -28,11 +27,9 @@ import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.model.session.WebSessionProvider;
|
||||
import io.cloudbeaver.server.WebAppUtils;
|
||||
import io.cloudbeaver.server.graphql.GraphQLEndpoint;
|
||||
import io.cloudbeaver.server.graphql.GraphQLLoggerUtil;
|
||||
import io.cloudbeaver.service.security.SMUtils;
|
||||
import io.cloudbeaver.utils.ServletAppUtils;
|
||||
import io.cloudbeaver.utils.WebDataSourceUtils;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
@@ -164,7 +161,7 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static WebConnectionInfo getWebConnection(@NotNull WebSession session, @Nullable String projectId, @NotNull String connectionId) throws DBWebException {
|
||||
public static WebConnectionInfo getWebConnection(@NotNull WebSession session, @NotNull String projectId, @NotNull String connectionId) throws DBWebException {
|
||||
return WebDataSourceUtils.getWebConnectionInfo(session, projectId, connectionId);
|
||||
}
|
||||
|
||||
@@ -343,27 +340,16 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
|
||||
}
|
||||
}
|
||||
}
|
||||
// Perform any checks before action call
|
||||
protected void beforeWebActionCall(WebAction webAction, Method method, Object[] args) throws DBException {
|
||||
|
||||
GraphQLContext graphQlContext = this.env.getGraphQlContext();
|
||||
HttpServletRequest request = graphQlContext.get("request");
|
||||
if (request == null) {
|
||||
return;
|
||||
}
|
||||
String sessionId = GraphQLLoggerUtil.getSmSessionId(request);
|
||||
String userId = GraphQLLoggerUtil.getUserId(request);
|
||||
String loggerMessage = GraphQLLoggerUtil.buildLoggerMessage(sessionId, userId, method, args);
|
||||
}
|
||||
|
||||
log.debug("API > " + method.getName() + loggerMessage);
|
||||
|
||||
setLogContext(method, args);
|
||||
}
|
||||
|
||||
protected void afterWebActionCall(WebAction webAction, Method method, Object[] args) throws DBException {
|
||||
Log.setContext(null);
|
||||
}
|
||||
// Perform any checks before action call
|
||||
protected void beforeWebActionCall(WebAction webAction, Method method, Object[] args) throws DBException {
|
||||
setLogContext(method, args);
|
||||
}
|
||||
|
||||
protected void afterWebActionCall(WebAction webAction, Method method, Object[] args) throws DBException {
|
||||
Log.setContext(null);
|
||||
}
|
||||
|
||||
protected void setLogContext(Method method, @Nullable Object[] args) {
|
||||
|
||||
+6
-3
@@ -16,7 +16,10 @@
|
||||
*/
|
||||
package io.cloudbeaver.service.core;
|
||||
|
||||
import io.cloudbeaver.*;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebAction;
|
||||
import io.cloudbeaver.WebObjectId;
|
||||
import io.cloudbeaver.WebProjectAction;
|
||||
import io.cloudbeaver.model.*;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.service.DBWService;
|
||||
@@ -116,7 +119,7 @@ public interface DBWServiceCore extends DBWService {
|
||||
@NotNull WebSession webSession,
|
||||
@Nullable String projectId,
|
||||
@NotNull String connectionId,
|
||||
@WebParameterSecure @Nullable Map<String, Object> authProperties,
|
||||
@Nullable Map<String, Object> authProperties,
|
||||
@Nullable List<WebNetworkHandlerConfigInput> networkCredentials,
|
||||
boolean saveCredentials,
|
||||
boolean sharedCredentials,
|
||||
@@ -127,7 +130,7 @@ public interface DBWServiceCore extends DBWService {
|
||||
WebConnectionInfo createConnection(
|
||||
@NotNull WebSession webSession,
|
||||
@Nullable @WebObjectId String projectId,
|
||||
@WebParameterSecure @NotNull Map<String, Object> connectionConfig
|
||||
@NotNull Map<String, Object> connectionConfig
|
||||
) throws DBWebException;
|
||||
|
||||
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_DATASOURCES_EDIT})
|
||||
|
||||
+4
-9
@@ -339,7 +339,6 @@ public class WebServiceCore implements DBWServiceCore {
|
||||
if (e instanceof DBCConnectException) {
|
||||
Throwable rootCause = CommonUtils.getRootCause(e);
|
||||
if (rootCause instanceof ClassNotFoundException) {
|
||||
log.error(e);
|
||||
throwDriverNotFoundException(dataSourceContainer);
|
||||
}
|
||||
}
|
||||
@@ -373,7 +372,6 @@ public class WebServiceCore implements DBWServiceCore {
|
||||
if (saveCredentials) {
|
||||
// Save all passed credentials in the datasource container
|
||||
WebDataSourceUtils.saveAuthProperties(
|
||||
webSession.getProgressMonitor(),
|
||||
dataSourceContainer,
|
||||
dataSourceContainer.getConnectionConfiguration(),
|
||||
authProperties,
|
||||
@@ -497,8 +495,6 @@ public class WebServiceCore implements DBWServiceCore {
|
||||
WebSessionProjectImpl project = getProjectById(webSession, projectId);
|
||||
WebConnectionConfig configInput = project.getConnectionConfigInput(connectionConfig);
|
||||
|
||||
configInput.setSaveCredentials(true); // It is used in createConnectionFromConfig
|
||||
|
||||
DataSourceDescriptor dataSource = (DataSourceDescriptor) WebDataSourceUtils.getLocalOrGlobalDataSource(
|
||||
webSession, projectId, configInput.getConnectionId());
|
||||
|
||||
@@ -530,7 +526,6 @@ public class WebServiceCore implements DBWServiceCore {
|
||||
}
|
||||
}
|
||||
WebDataSourceUtils.saveAuthProperties(
|
||||
webSession.getProgressMonitor(),
|
||||
testDataSource,
|
||||
testDataSource.getConnectionConfiguration(),
|
||||
configInput.getCredentials(),
|
||||
@@ -539,7 +534,7 @@ public class WebServiceCore implements DBWServiceCore {
|
||||
true
|
||||
);
|
||||
} else {
|
||||
testDataSource = project.getDataSourceContainerFromInput(configInput);
|
||||
testDataSource = project.getDataSourceContainerFromInput(connectionConfig);
|
||||
}
|
||||
validateDriverLibrariesPresence(testDataSource);
|
||||
webSession.provideAuthParameters(
|
||||
@@ -559,7 +554,6 @@ public class WebServiceCore implements DBWServiceCore {
|
||||
if (ct.getConnectError() instanceof DBCConnectException error) {
|
||||
Throwable rootCause = CommonUtils.getRootCause(error);
|
||||
if (rootCause instanceof ClassNotFoundException) {
|
||||
log.error(error);
|
||||
throwDriverNotFoundException(testDataSource);
|
||||
}
|
||||
}
|
||||
@@ -640,7 +634,7 @@ public class WebServiceCore implements DBWServiceCore {
|
||||
WebConnectionInfo connectionInfo = project.getWebConnectionInfo(connectionId);
|
||||
|
||||
DBPDataSourceContainer dataSourceContainer = connectionInfo.getDataSourceContainer();
|
||||
WebDataSourceUtils.disconnectDataSource(webSession, dataSourceContainer);
|
||||
boolean disconnected = WebDataSourceUtils.disconnectDataSource(webSession, dataSourceContainer);
|
||||
return connectionInfo;
|
||||
}
|
||||
|
||||
@@ -760,7 +754,8 @@ public class WebServiceCore implements DBWServiceCore {
|
||||
}
|
||||
}
|
||||
|
||||
private static void throwDriverNotFoundException(@NotNull DBPDataSourceContainer container) throws DBWebException {
|
||||
@NotNull
|
||||
private static String throwDriverNotFoundException(@NotNull DBPDataSourceContainer container) throws DBWebException {
|
||||
throw new DBWebException("Driver files for %s are not found. Please ask the administrator to download it."
|
||||
.formatted(container.getDriver().getName()));
|
||||
}
|
||||
|
||||
+4
-9
@@ -17,7 +17,10 @@
|
||||
package io.cloudbeaver.service.navigator.impl;
|
||||
|
||||
|
||||
import io.cloudbeaver.*;
|
||||
import io.cloudbeaver.BaseWebProjectImpl;
|
||||
import io.cloudbeaver.DBWConstants;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebServiceUtils;
|
||||
import io.cloudbeaver.model.WebCommandContext;
|
||||
import io.cloudbeaver.model.WebConnectionInfo;
|
||||
import io.cloudbeaver.model.rm.DBNAbstractResourceManagerNode;
|
||||
@@ -108,14 +111,6 @@ public class WebServiceNavigator implements DBWServiceNavigator {
|
||||
Set<String> nodeIds = new HashSet<>(); // filter duplicate node ids
|
||||
|
||||
for (DBNNode node : nodeChildren) {
|
||||
// TODO: find a better way to filter out empty folder nodes for non-admin users
|
||||
if (node instanceof DBNLocalFolder localFolderNode &&
|
||||
localFolderNode.getDataSourceRegistry() instanceof WebGlobalProjectRegistryProxy globalProjectRegistryProxy
|
||||
) {
|
||||
if (!globalProjectRegistryProxy.getAllFolders().contains(localFolderNode.getFolder())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (node instanceof DBNDatabaseFolder folderNode && CommonUtils.isEmpty(folderNode.getMeta().getChildren(null))) {
|
||||
// Skip empty folders. Folder may become empty if their nested elements are provided by UI plugins.
|
||||
continue;
|
||||
|
||||
+1
-2
@@ -101,8 +101,7 @@ public interface DBWServiceSQL extends DBWService {
|
||||
@Nullable String resultId,
|
||||
@Nullable WebSQLDataFilter filter,
|
||||
@Nullable WebDataFormat dataFormat,
|
||||
boolean readLogs,
|
||||
boolean useEvents
|
||||
boolean readLogs
|
||||
) throws DBException;
|
||||
|
||||
@WebAction
|
||||
|
||||
+3
-3
@@ -48,10 +48,10 @@ import org.jkiss.dbeaver.model.struct.rdb.DBSSchema;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSTransactionalCountEvent;
|
||||
import org.jkiss.dbeaver.utils.RuntimeUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
import org.jkiss.utils.StandardConstants;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@@ -69,12 +69,12 @@ public class WebSQLContextInfo implements WebSessionProvider {
|
||||
private final transient WebSQLProcessor processor;
|
||||
private final String id;
|
||||
private final String projectId;
|
||||
private final transient Map<String, WebSQLResultsInfo> resultInfoMap = new HashMap<>();
|
||||
private final Map<String, WebSQLResultsInfo> resultInfoMap = new HashMap<>();
|
||||
|
||||
private final AtomicInteger resultId = new AtomicInteger();
|
||||
|
||||
public static final DateTimeFormatter ISO_DATE_FORMAT = DateTimeFormatter.ofPattern(DBConstants.DEFAULT_ISO_TIMESTAMP_FORMAT)
|
||||
.withZone(ZoneId.of("UTC"));
|
||||
.withZone(StandardConstants.ZONE_ID_UTC);
|
||||
|
||||
public WebSQLContextInfo(
|
||||
WebSQLProcessor processor, String id, String catalogName, String schemaName, String projectId
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ public class WebSQLExecuteInfo {
|
||||
@Property
|
||||
public String getOriginalQuery() {
|
||||
//TODO implement
|
||||
return null; // should return the original query without data filters
|
||||
return null;
|
||||
}
|
||||
|
||||
@Property
|
||||
|
||||
-98
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.service.sql;
|
||||
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.model.WebAsyncTaskInfo;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.service.sql.messages.WebSQLMessages;
|
||||
import io.cloudbeaver.websocket.event.task.WSSessionTaskQueryParamsConfirmationEvent;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.data.DBDDataReceiver;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.model.sql.SQLParametersProviderBase;
|
||||
import org.jkiss.dbeaver.model.sql.SQLQuery;
|
||||
import org.jkiss.dbeaver.model.sql.SQLQueryParameter;
|
||||
import org.jkiss.dbeaver.model.sql.SQLScriptContext;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class WebSQLParametersProvider extends SQLParametersProviderBase {
|
||||
private static final Log log = Log.getLog(WebSQLParametersProvider.class);
|
||||
@NotNull
|
||||
private final WebSession webSession;
|
||||
@NotNull
|
||||
private final WebAsyncTaskInfo taskInfo;
|
||||
|
||||
public WebSQLParametersProvider(@NotNull WebSession webSession, @NotNull WebAsyncTaskInfo taskInfo) {
|
||||
this.webSession = webSession;
|
||||
this.taskInfo = taskInfo;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected Boolean collectAndAssignVariables(
|
||||
@NotNull SQLScriptContext scriptContext,
|
||||
@NotNull SQLQuery sqlStatement,
|
||||
@NotNull List<SQLQueryParameter> parameters,
|
||||
@NotNull Supplier<DBDDataReceiver> dataReceiverSupplier
|
||||
) {
|
||||
Map<String, Object> unsetParams = new LinkedHashMap<>();
|
||||
for (SQLQueryParameter param : parameters) {
|
||||
if (!param.isVariableSet()) {
|
||||
// Empty value for now, maybe in future we can set some defaults here
|
||||
unsetParams.put(param.getName(), "");
|
||||
}
|
||||
}
|
||||
if (unsetParams.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
CompletableFuture<Map<String, Object>> completableFuture = new CompletableFuture<>();
|
||||
WSEvent event = new WSSessionTaskQueryParamsConfirmationEvent(
|
||||
taskInfo.getId(),
|
||||
WebSQLMessages.model_web_dialog_sql_param_title,
|
||||
"",
|
||||
sqlStatement.getText(),
|
||||
unsetParams
|
||||
);
|
||||
try {
|
||||
Map<String, Object> response = WebSQLUtils.requestConfirmation(webSession, taskInfo, event, completableFuture);
|
||||
// Save values back to script context
|
||||
for (SQLQueryParameter param : parameters) {
|
||||
if (response != null && response.containsKey(param.getName())) {
|
||||
String strValue = JSONUtils.getString(response, param.getName());
|
||||
param.setValue(strValue);
|
||||
param.setVariableSet(true);
|
||||
scriptContext.setVariable(param.getName(), strValue);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
} catch (DBWebException e) {
|
||||
log.error("Error getting SQL parameter values: " + e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
-159
@@ -17,16 +17,11 @@
|
||||
package io.cloudbeaver.service.sql;
|
||||
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.model.WebAsyncTaskInfo;
|
||||
import io.cloudbeaver.model.WebConnectionInfo;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.model.session.WebSessionPreferenceStore;
|
||||
import io.cloudbeaver.model.session.WebSessionProvider;
|
||||
import io.cloudbeaver.server.WebAppUtils;
|
||||
import io.cloudbeaver.server.jobs.SqlOutputLogReaderJob;
|
||||
import io.cloudbeaver.service.sql.messages.WebSQLMessages;
|
||||
import io.cloudbeaver.websocket.event.task.WSSessionTaskConfirmationRequestEvent;
|
||||
import io.cloudbeaver.websocket.event.task.WSSessionTaskQueryConfirmationRequestEvent;
|
||||
import org.eclipse.jface.text.Document;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
@@ -53,22 +48,17 @@ import org.jkiss.dbeaver.model.sql.parser.SQLParserContext;
|
||||
import org.jkiss.dbeaver.model.sql.parser.SQLRuleManager;
|
||||
import org.jkiss.dbeaver.model.sql.parser.SQLScriptParser;
|
||||
import org.jkiss.dbeaver.model.struct.*;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSTransactionalCountEvent;
|
||||
import org.jkiss.dbeaver.registry.confirmation.ConfirmationConstants;
|
||||
import org.jkiss.dbeaver.registry.confirmation.ConfirmationDescriptor;
|
||||
import org.jkiss.dbeaver.registry.confirmation.ConfirmationRegistry;
|
||||
import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
import org.jkiss.utils.ArrayUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -176,9 +166,7 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
@Nullable WebSQLDataFilter filter,
|
||||
@Nullable WebDataFormat dataFormat,
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull WebAsyncTaskInfo asyncTask,
|
||||
boolean readLogs,
|
||||
boolean useEvents
|
||||
boolean readLogs
|
||||
) throws DBWebException, DBCException {
|
||||
if (filter == null) {
|
||||
// Use default filter
|
||||
@@ -187,9 +175,7 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
long startTime = System.currentTimeMillis();
|
||||
WebSQLExecuteInfo executeInfo = new WebSQLExecuteInfo();
|
||||
|
||||
WebSQLParametersProvider parametersProvider = new WebSQLParametersProvider(webSession, asyncTask);
|
||||
|
||||
var dataContainer = new WebSQLQueryDataContainer(connection.getDataSource(), syntaxManager, sql, parametersProvider);
|
||||
var dataContainer = new WebSQLQueryDataContainer(connection.getDataSource(), syntaxManager, sql);
|
||||
|
||||
DBCExecutionContext context = getExecutionContext(dataContainer);
|
||||
|
||||
@@ -216,11 +202,9 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
|
||||
SQLScriptElement element = SQLScriptParser.extractActiveQuery(parserContext, 0, sql.length());
|
||||
|
||||
boolean isGenerated = false;
|
||||
if (element instanceof SQLControlCommand command) {
|
||||
SQLControlResult controlResult = dataContainer.getScriptContext().executeControlCommand(monitor, command);
|
||||
if (controlResult.getTransformed() != null) {
|
||||
isGenerated = true;
|
||||
element = controlResult.getTransformed();
|
||||
} else {
|
||||
WebSQLQueryResults stats = new WebSQLQueryResults(webSession, dataFormat);
|
||||
@@ -228,18 +212,6 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
}
|
||||
}
|
||||
if (element instanceof SQLQuery mainQuery) {
|
||||
|
||||
if (useEvents) {
|
||||
// fill query with parameters
|
||||
mainQuery.setParameters(SQLScriptParser.parseParametersAndVariables(parserContext, 0, mainQuery.getLength()));
|
||||
boolean isConfirmed =
|
||||
dataContainer.getScriptContext().fillQueryParameters(mainQuery, () -> null, true) &&
|
||||
confirmDangerousQueryIfNeeded(mainQuery.getScriptElements(), asyncTask, isGenerated);
|
||||
if (!isConfirmed) {
|
||||
throw new DBWebException("Query execution was cancelled by user");
|
||||
}
|
||||
}
|
||||
|
||||
DBExecUtils.tryExecuteRecover(monitor, connection.getDataSource(), param -> {
|
||||
try (DBCSession session = context.openSession(monitor, resolveQueryPurpose(dataFilter), "Execute SQL")) {
|
||||
List<SQLScriptElement> sqlQueries = mainQuery.getScriptElements();
|
||||
@@ -297,6 +269,8 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
sqlOutputLogReaderJob.join();
|
||||
}
|
||||
fillQueryResults(contextInfo, dataContainer, dbStat, hasResultSet, executeInfo, webDataFilter, dataFilter, dataFormat);
|
||||
} catch (DBException e) {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -338,9 +312,7 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
DBDDataFilter dataFilter = filter.makeDataFilter((resultId == null ? null : contextInfo.getResults(resultId)));
|
||||
DBExecUtils.tryExecuteRecover(monitor, connection.getDataSource(), param -> {
|
||||
try (DBCSession session = executionContext.openSession(monitor, resolveQueryPurpose(dataFilter), "Read data from container")) {
|
||||
try (
|
||||
WebSQLQueryDataReceiver dataReceiver = new WebSQLQueryDataReceiver(contextInfo, dataContainer, dataFormat, dataFilter)
|
||||
) {
|
||||
try (WebSQLQueryDataReceiver dataReceiver = new WebSQLQueryDataReceiver(contextInfo, dataContainer, dataFormat)) {
|
||||
DBCStatistics statistics = dataContainer.readData(
|
||||
new WebExecutionSource(dataContainer, executionContext, this),
|
||||
session,
|
||||
@@ -357,12 +329,14 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
results.setResultSet(resultSet);
|
||||
|
||||
executeInfo.setResults(new WebSQLQueryResults[]{results});
|
||||
setResultFilterText(session.getDataSource(), executeInfo, dataFilter);
|
||||
setResultFilterText(dataContainer, session.getDataSource(), executeInfo, dataFilter);
|
||||
executeInfo.setFullQuery(statistics.getQueryText());
|
||||
if (resultSet != null && resultSet.getRowsWithMetaData() != null && resultSet.getResultsInfo() != null) {
|
||||
resultSet.getResultsInfo().setQueryText(statistics.getQueryText());
|
||||
executeInfo.setStatusMessage(resultSet.getRowsWithMetaData().size() + " row(s) fetched");
|
||||
}
|
||||
} catch (DBException e) {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -402,7 +376,7 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
for (var rowIdentifier : rowIdentifierList) {
|
||||
Map<DBSDataManipulator.ExecuteBatch, Object[]> resultBatches = new LinkedHashMap<>();
|
||||
DBSDataManipulator dataManipulator = generateUpdateResultsDataBatch(
|
||||
monitor, resultsInfo, rowIdentifier, updatedRows, deletedRows, addedRows, resultBatches, keyReceiver);
|
||||
monitor, resultsInfo, rowIdentifier, updatedRows, deletedRows, addedRows, dataFormat, resultBatches, keyReceiver);
|
||||
|
||||
DBCExecutionContext executionContext = getExecutionContext(dataManipulator);
|
||||
try (DBCSession session = executionContext.openSession(monitor, DBCExecutionPurpose.USER, "Update data in container")) {
|
||||
@@ -503,7 +477,7 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
@NotNull Set<WebSQLQueryResultSetRow> newResultSetRows,
|
||||
@Nullable WebDataFormat dataFormat,
|
||||
@NotNull DBRProgressMonitor monitor)
|
||||
throws DBException {
|
||||
throws DBCException {
|
||||
try (DBCSession session = getExecutionContext().openSession(
|
||||
monitor,
|
||||
DBCExecutionPurpose.UTIL,
|
||||
@@ -579,8 +553,9 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
@NotNull String resultsId,
|
||||
@Nullable List<WebSQLResultsRow> updatedRows,
|
||||
@Nullable List<WebSQLResultsRow> deletedRows,
|
||||
@Nullable List<WebSQLResultsRow> addedRows
|
||||
) throws DBException {
|
||||
@Nullable List<WebSQLResultsRow> addedRows,
|
||||
@Nullable WebDataFormat dataFormat) throws DBException
|
||||
{
|
||||
Map<DBSDataManipulator.ExecuteBatch, Object[]> resultBatches = new LinkedHashMap<>();
|
||||
|
||||
|
||||
@@ -596,7 +571,7 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
StringBuilder sqlBuilder = new StringBuilder();
|
||||
for (var rowIdentifier : rowIdentifierList) {
|
||||
DBSDataManipulator dataManipulator = generateUpdateResultsDataBatch(
|
||||
monitor, resultsInfo, rowIdentifier, updatedRows, deletedRows, addedRows, resultBatches, null);
|
||||
monitor, resultsInfo, rowIdentifier, updatedRows, deletedRows, addedRows, dataFormat, resultBatches, null);
|
||||
|
||||
List<DBEPersistAction> actions = new ArrayList<>();
|
||||
|
||||
@@ -622,6 +597,7 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
@Nullable List<WebSQLResultsRow> updatedRows,
|
||||
@Nullable List<WebSQLResultsRow> deletedRows,
|
||||
@Nullable List<WebSQLResultsRow> addedRows,
|
||||
@Nullable WebDataFormat dataFormat,
|
||||
@NotNull Map<DBSDataManipulator.ExecuteBatch, Object[]> resultBatches,
|
||||
@Nullable DBDDataReceiver keyReceiver)
|
||||
throws DBException
|
||||
@@ -881,6 +857,7 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
@NotNull
|
||||
public WebSQLExecutionPlan explainExecutionPlan(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull WebSQLContextInfo contextInfo,
|
||||
@NotNull String sql,
|
||||
@NotNull Map<String, Object> configuration) throws DBWebException {
|
||||
|
||||
@@ -905,6 +882,8 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
DBCQueryPlannerConfiguration planConfig = new DBCQueryPlannerConfiguration();
|
||||
planConfig.getParameters().putAll(configuration);
|
||||
dbcPlan[0] = planner.planQueryExecution(session, sql, planConfig);
|
||||
} catch (DBException e) {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
});
|
||||
} catch (DBException e) {
|
||||
@@ -944,14 +923,16 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
private void readCellDataValue(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull WebSQLResultsInfo resultsInfo,
|
||||
@NotNull WebSQLResultsRow row,
|
||||
@Nullable WebSQLResultsRow row,
|
||||
@NotNull WebSQLCellValueReceiver dataReceiver
|
||||
) throws DBException {
|
||||
DBSDataContainer dataContainer = resultsInfo.getDataContainer();
|
||||
DBCExecutionContext executionContext = getExecutionContext(dataContainer);
|
||||
try (DBCSession session = executionContext.openSession(monitor, DBCExecutionPurpose.USER, "Generate data update batches")) {
|
||||
DBDDataFilter dataFilter = new DBDDataFilter();
|
||||
addKeyAttributes(resultsInfo, row, dataContainer, session, dataFilter);
|
||||
if (!resultsInfo.isSingleRow()) {
|
||||
addKeyAttributes(resultsInfo, row, dataContainer, session, dataFilter);
|
||||
}
|
||||
WebExecutionSource executionSource = new WebExecutionSource(dataContainer, executionContext, this);
|
||||
dataContainer.readData(
|
||||
executionSource, session, dataReceiver, dataFilter,
|
||||
@@ -961,16 +942,12 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
|
||||
private void addKeyAttributes(
|
||||
@NotNull WebSQLResultsInfo resultsInfo,
|
||||
@NotNull WebSQLResultsRow row,
|
||||
@Nullable WebSQLResultsRow row,
|
||||
@NotNull DBSDataContainer dataContainer,
|
||||
@NotNull DBCSession session,
|
||||
@NotNull DBDDataFilter dataFilter
|
||||
) throws DBException {
|
||||
DBDRowIdentifier rowIdentifier = resultsInfo.getDefaultRowIdentifier();
|
||||
if (rowIdentifier == null || rowIdentifier.isIncomplete()) {
|
||||
return;
|
||||
}
|
||||
DBDAttributeBinding[] keyAttributes = rowIdentifier.getAttributes().toArray(new DBDAttributeBinding[0]);
|
||||
DBDAttributeBinding[] keyAttributes = resultsInfo.getDefaultRowIdentifier().getAttributes().toArray(new DBDAttributeBinding[0]);
|
||||
Object[] rowValues = new Object[keyAttributes.length];
|
||||
List<DBDAttributeConstraint> constraints = new ArrayList<>();
|
||||
for (int i = 0; i < keyAttributes.length; i++) {
|
||||
@@ -1074,16 +1051,10 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
if (resultSet == null) {
|
||||
break;
|
||||
}
|
||||
try (
|
||||
WebSQLQueryDataReceiver dataReceiver = new WebSQLQueryDataReceiver(
|
||||
contextInfo,
|
||||
dataContainer,
|
||||
dataFormat,
|
||||
dataFilter
|
||||
)
|
||||
) {
|
||||
try (WebSQLQueryDataReceiver dataReceiver = new WebSQLQueryDataReceiver(contextInfo, dataContainer, dataFormat)) {
|
||||
readResultSet(dbStat.getSession(), resultSet, webDataFilter, dataReceiver);
|
||||
results.setResultSet(dataReceiver.getResultSet());
|
||||
dataReceiver.getResultSet().getResultsInfo().setQueryText(resultSet.getSourceStatement().getQueryString());
|
||||
}
|
||||
}
|
||||
resultList.add(results);
|
||||
@@ -1103,15 +1074,11 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
}
|
||||
executeInfo.setResults(resultList.toArray(new WebSQLQueryResults[0]));
|
||||
|
||||
setResultFilterText(dbStat.getSession().getDataSource(), executeInfo, dataFilter);
|
||||
setResultFilterText(dataContainer, dbStat.getSession().getDataSource(), executeInfo, dataFilter);
|
||||
executeInfo.setFullQuery(dbStat.getQueryString());
|
||||
}
|
||||
|
||||
private void setResultFilterText(
|
||||
@NotNull DBPDataSource dataSource,
|
||||
@NotNull WebSQLExecuteInfo executeInfo,
|
||||
@NotNull DBDDataFilter filter
|
||||
) throws DBException {
|
||||
private void setResultFilterText(@NotNull DBSDataContainer dataContainer, @NotNull DBPDataSource dataSource, @NotNull WebSQLExecuteInfo executeInfo, @NotNull DBDDataFilter filter) throws DBException {
|
||||
if (!filter.getConstraints().isEmpty() || !CommonUtils.isEmpty(filter.getWhere())) {
|
||||
StringBuilder where = new StringBuilder();
|
||||
SQLUtils.appendConditionString(
|
||||
@@ -1124,13 +1091,8 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private void readResultSet(
|
||||
@NotNull DBCSession session,
|
||||
@NotNull DBCResultSet dbResult,
|
||||
@NotNull WebSQLDataFilter filter,
|
||||
@NotNull WebSQLQueryDataReceiver dataReceiver
|
||||
) throws DBException {
|
||||
DBDDataReceiver.startFetchWorkflow(dataReceiver, session, dbResult, filter.getOffset(), filter.getLimit());
|
||||
private void readResultSet(@NotNull DBCSession session, @NotNull DBCResultSet dbResult, @NotNull WebSQLDataFilter filter, @NotNull WebSQLQueryDataReceiver dataReceiver) throws DBCException {
|
||||
dataReceiver.fetchStart(session, dbResult, filter.getOffset(), filter.getLimit());
|
||||
int rowCount = 0;
|
||||
while (dbResult.nextRow()) {
|
||||
if (rowCount > filter.getLimit()) {
|
||||
@@ -1140,6 +1102,7 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
dataReceiver.fetchRow(session, dbResult);
|
||||
rowCount++;
|
||||
}
|
||||
dataReceiver.fetchEnd(session, dbResult);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1177,6 +1140,7 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
if (keyValue == null) {
|
||||
continue;
|
||||
}
|
||||
boolean updated = false;
|
||||
if (!CommonUtils.isEmpty(keyAttribute.getName())) {
|
||||
DBDAttributeBinding binding = DBUtils.findObject(resultsAttributes, keyAttribute.getName());
|
||||
if (binding != null) {
|
||||
@@ -1247,7 +1211,8 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
|
||||
private Object setCellRowValue(Object cellRow, WebSession webSession, DBCSession dbcSession, DBDAttributeBinding allAttributes, boolean withoutExecution)
|
||||
throws DBException {
|
||||
if (cellRow instanceof Map<?, ?> variables) {
|
||||
if (cellRow instanceof Map<?, ?>) {
|
||||
Map<String, Object> variables = (Map<String, Object>) cellRow;
|
||||
if (variables.get(FILE_ID) != null) {
|
||||
Path path = WebAppUtils.getWebPlatform()
|
||||
.getTempFolder(webSession.getProgressMonitor(), TEMP_FILE_FOLDER)
|
||||
@@ -1264,93 +1229,4 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
}
|
||||
return convertInputCellValue(dbcSession, allAttributes, cellRow, withoutExecution);
|
||||
}
|
||||
|
||||
private boolean confirmDangerousQueryIfNeeded(
|
||||
@NotNull List<SQLScriptElement> scriptElements,
|
||||
@NotNull WebAsyncTaskInfo asyncTask,
|
||||
boolean isGenerated
|
||||
) throws DBWebException {
|
||||
Boolean skipConfirmations = webSession.getAttribute(WebSQLConstants.SKIP_TASK_CONFIRMATIONS_ATTR);
|
||||
if (skipConfirmations != null && skipConfirmations) {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean hasGeneratedUpdates = false;
|
||||
boolean hasDangerousUpdates = false;
|
||||
boolean hasDropStatement = false;
|
||||
String title = null;
|
||||
String message = null;
|
||||
String queryPreview = null;
|
||||
if (isGenerated) {
|
||||
Set<SQLQueryCategory> categories = SQLQueryCategory.categorizeScript(scriptElements);
|
||||
hasGeneratedUpdates = categories.contains(SQLQueryCategory.DDL) ||
|
||||
categories.contains(SQLQueryCategory.DML) ||
|
||||
categories.contains(SQLQueryCategory.UNKNOWN);
|
||||
title = WebSQLMessages.model_web_ai_query_confirmation_title;
|
||||
message = WebSQLMessages.model_web_ai_query_confirmation_message;
|
||||
queryPreview = scriptElements.stream()
|
||||
.map(SQLScriptElement::getText)
|
||||
.collect(Collectors.joining("\n\n"));
|
||||
} else {
|
||||
WebSessionPreferenceStore store = webSession.getUserPreferenceStore();
|
||||
boolean confirmDangerousQueries = store.getUserPreferenceBoolean(ConfirmationConstants.CONFIRM_DANGER_SQL_KEY, true);
|
||||
boolean confirmDropQueries = store.getUserPreferenceBoolean(ConfirmationConstants.CONFIRM_DROP_SQL_KEY, true);
|
||||
for (SQLScriptElement scriptElement : scriptElements) {
|
||||
if (scriptElement instanceof SQLQuery sqlQuery) {
|
||||
if (confirmDangerousQueries && sqlQuery.isDeleteUpdateDangerous()) {
|
||||
hasDangerousUpdates = true;
|
||||
ConfirmationDescriptor descriptor = ConfirmationRegistry.getInstance()
|
||||
.getConfirmation(ConfirmationConstants.CONFIRM_DANGER_SQL_ID);
|
||||
title = descriptor.getLocalizedTitle(webSession.getLocale());
|
||||
var entityMetadata = sqlQuery.getEntityMetadata(false);
|
||||
message = MessageFormat.format(
|
||||
descriptor.getLocalizedMessage(webSession.getLocale()),
|
||||
sqlQuery.getType().name(),
|
||||
entityMetadata != null ? entityMetadata.getEntityName() : "multiple tables"
|
||||
);
|
||||
break;
|
||||
}
|
||||
if (confirmDropQueries && sqlQuery.isDropDangerous()) {
|
||||
hasDropStatement = true;
|
||||
ConfirmationDescriptor descriptor = ConfirmationRegistry.getInstance()
|
||||
.getConfirmation(ConfirmationConstants.CONFIRM_DROP_SQL_ID);
|
||||
title = descriptor.getLocalizedTitle(webSession.getLocale());
|
||||
message = MessageFormat.format(
|
||||
descriptor.getLocalizedMessage(webSession.getLocale()),
|
||||
sqlQuery.getText()
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasGeneratedUpdates && !hasDangerousUpdates && !hasDropStatement) {
|
||||
return true;
|
||||
} else {
|
||||
WSEvent confirmationEvent = createConfirmationEvent(asyncTask, queryPreview, title, message);
|
||||
CompletableFuture<Boolean> confirmationFuture = new CompletableFuture<>();
|
||||
return CommonUtils.toBoolean(WebSQLUtils.requestConfirmation(webSession, asyncTask, confirmationEvent, confirmationFuture));
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private WSEvent createConfirmationEvent(
|
||||
@NotNull WebAsyncTaskInfo asyncTask,
|
||||
@Nullable String query,
|
||||
@NotNull String title,
|
||||
@NotNull String message
|
||||
) {
|
||||
WSEvent confirmationEvent;
|
||||
if (query != null) {
|
||||
confirmationEvent = new WSSessionTaskQueryConfirmationRequestEvent(
|
||||
asyncTask.getId(), title, message, query
|
||||
);
|
||||
} else {
|
||||
confirmationEvent = new WSSessionTaskConfirmationRequestEvent(
|
||||
asyncTask.getId(), title, message
|
||||
);
|
||||
}
|
||||
return confirmationEvent;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-22
@@ -26,7 +26,6 @@ import org.jkiss.dbeaver.model.DBUtils;
|
||||
import org.jkiss.dbeaver.model.data.DBDDataFilter;
|
||||
import org.jkiss.dbeaver.model.data.DBDDataReceiver;
|
||||
import org.jkiss.dbeaver.model.exec.*;
|
||||
import org.jkiss.dbeaver.model.sql.SQLParametersProvider;
|
||||
import org.jkiss.dbeaver.model.sql.SQLQuery;
|
||||
import org.jkiss.dbeaver.model.sql.SQLScriptContext;
|
||||
import org.jkiss.dbeaver.model.sql.SQLSyntaxManager;
|
||||
@@ -49,19 +48,13 @@ public class WebSQLQueryDataContainer implements DBSDataContainer, DBPContextPro
|
||||
private final String query;
|
||||
private final SQLQueryDataContainer queryDataContainer;
|
||||
|
||||
public WebSQLQueryDataContainer(
|
||||
@NotNull DBPDataSource dataSource,
|
||||
@NotNull SQLSyntaxManager syntaxManager,
|
||||
@NotNull String query,
|
||||
@NotNull SQLParametersProvider parametersProvider
|
||||
) {
|
||||
public WebSQLQueryDataContainer(DBPDataSource dataSource, SQLSyntaxManager syntaxManager, String query) {
|
||||
this.dataSource = dataSource;
|
||||
this.syntaxManager = syntaxManager;
|
||||
this.query = query;
|
||||
|
||||
SQLScriptContext scriptContext = new SQLScriptContext(null,
|
||||
this, null, new PrintWriter(System.err, true), parametersProvider
|
||||
);
|
||||
this, null, new PrintWriter(System.err, true), null);
|
||||
queryDataContainer = new SQLQueryDataContainer(this, new SQLQuery(dataSource, query), scriptContext, log);
|
||||
}
|
||||
|
||||
@@ -111,25 +104,14 @@ public class WebSQLQueryDataContainer implements DBSDataContainer, DBPContextPro
|
||||
long maxRows,
|
||||
long flags,
|
||||
int fetchSize
|
||||
) throws DBException {
|
||||
) throws DBCException {
|
||||
return queryDataContainer.readData(source, session, dataReceiver, dataFilter, firstRow, maxRows, flags, fetchSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countData(@NotNull DBCExecutionSource source, @NotNull DBCSession session, @Nullable DBDDataFilter dataFilter, long flags) throws DBCException {
|
||||
try {
|
||||
String filteredQuery = dataFilter == null ? query : getDataSource().getSQLDialect().addFiltersToQuery(
|
||||
session.getProgressMonitor(),
|
||||
getDataSource(),
|
||||
query,
|
||||
dataFilter
|
||||
);
|
||||
|
||||
SQLQuery countQuery = new SQLQueryTransformerCount().transformQuery(
|
||||
dataSource,
|
||||
syntaxManager,
|
||||
new SQLQuery(dataSource, filteredQuery)
|
||||
);
|
||||
SQLQuery countQuery = new SQLQueryTransformerCount().transformQuery(dataSource, syntaxManager, new SQLQuery(dataSource, query));
|
||||
return DBUtils.countDataFromQuery(source, session, countQuery);
|
||||
} catch (DBException e) {
|
||||
throw new DBCException("Error executing row count", e);
|
||||
|
||||
+2
-16
@@ -19,7 +19,6 @@ package io.cloudbeaver.service.sql;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.utils.ServletAppUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBPDataKind;
|
||||
@@ -42,14 +41,9 @@ import java.util.stream.Collectors;
|
||||
class WebSQLQueryDataReceiver implements DBDDataReceiver {
|
||||
private static final Log log = Log.getLog(WebSQLQueryDataReceiver.class);
|
||||
|
||||
@NotNull
|
||||
private final WebSQLContextInfo contextInfo;
|
||||
@NotNull
|
||||
private final DBSDataContainer dataContainer;
|
||||
@Nullable
|
||||
private final WebDataFormat dataFormat;
|
||||
@Nullable
|
||||
private final DBDDataFilter dataFilter;
|
||||
private final WebSQLQueryResultSet webResultSet = new WebSQLQueryResultSet();
|
||||
|
||||
private DBDAttributeBinding[] bindings;
|
||||
@@ -57,16 +51,10 @@ class WebSQLQueryDataReceiver implements DBDDataReceiver {
|
||||
private List<WebSQLQueryResultSetRow> rows = new ArrayList<>();
|
||||
private final Number rowLimit;
|
||||
|
||||
WebSQLQueryDataReceiver(
|
||||
@NotNull WebSQLContextInfo contextInfo,
|
||||
@NotNull DBSDataContainer dataContainer,
|
||||
@Nullable WebDataFormat dataFormat,
|
||||
@Nullable DBDDataFilter dataFilter
|
||||
) {
|
||||
WebSQLQueryDataReceiver(WebSQLContextInfo contextInfo, DBSDataContainer dataContainer, WebDataFormat dataFormat) {
|
||||
this.contextInfo = contextInfo;
|
||||
this.dataContainer = dataContainer;
|
||||
this.dataFormat = dataFormat;
|
||||
this.dataFilter = dataFilter;
|
||||
rowLimit = ServletAppUtils.getServletApplication()
|
||||
.getAppConfiguration()
|
||||
.getResourceQuota(WebSQLConstants.QUOTA_PROP_ROW_LIMIT);
|
||||
@@ -174,8 +162,6 @@ class WebSQLQueryDataReceiver implements DBDDataReceiver {
|
||||
webResultSet.setReadOnlyInfo(contextInfo.getProcessor().getExecutionContext());
|
||||
|
||||
WebSQLResultsInfo resultsInfo = contextInfo.saveResult(dataContainer, trace, bindings, rows.size() == 1);
|
||||
resultsInfo.setDataFilter(dataFilter);
|
||||
resultsInfo.setQueryText(resultSet.getSourceStatement().getQueryString());
|
||||
webResultSet.setResultsInfo(resultsInfo);
|
||||
|
||||
boolean isSingleEntity = DBExecUtils.detectSingleSourceTable(bindings) != null;
|
||||
@@ -246,6 +232,6 @@ class WebSQLQueryDataReceiver implements DBDDataReceiver {
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// no-op
|
||||
rows.clear();
|
||||
}
|
||||
}
|
||||
|
||||
+3
-34
@@ -16,7 +16,6 @@
|
||||
*/
|
||||
package io.cloudbeaver.service.sql;
|
||||
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.model.WebAsyncTaskInfo;
|
||||
import io.cloudbeaver.model.app.ServletAppConfiguration;
|
||||
import io.cloudbeaver.model.session.WebAsyncTaskProcessor;
|
||||
@@ -37,7 +36,6 @@ import org.jkiss.dbeaver.model.gis.GisTransformUtils;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.struct.DBSAttributeBase;
|
||||
import org.jkiss.dbeaver.model.struct.DBSTypedObject;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
|
||||
import org.jkiss.dbeaver.utils.ContentUtils;
|
||||
import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
import org.jkiss.utils.Base64;
|
||||
@@ -47,9 +45,6 @@ import java.io.StringWriter;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* Web SQL utils.
|
||||
@@ -282,10 +277,8 @@ public class WebSQLUtils {
|
||||
@Nullable String resultId,
|
||||
@Nullable WebSQLDataFilter filter,
|
||||
@Nullable WebDataFormat dataFormat,
|
||||
boolean readLogs,
|
||||
boolean useEvents
|
||||
boolean readLogs
|
||||
) {
|
||||
final WebAsyncTaskInfo task = webSession.createAsyncTask("SQL execute");
|
||||
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<>() {
|
||||
@Override
|
||||
public void run(DBRProgressMonitor monitor) throws InvocationTargetException {
|
||||
@@ -293,8 +286,7 @@ public class WebSQLUtils {
|
||||
monitor.beginTask("Execute query", 1);
|
||||
monitor.subTask("Process query " + sql);
|
||||
WebSQLExecuteInfo executeResults = contextInfo.getProcessor().processQuery(
|
||||
monitor, contextInfo, sql, resultId, filter, dataFormat, webSession, task, readLogs, useEvents
|
||||
);
|
||||
monitor, contextInfo, sql, resultId, filter, dataFormat, webSession, readLogs);
|
||||
this.result = executeResults.getStatusMessage();
|
||||
this.extendedResults = executeResults;
|
||||
} catch (Throwable e) {
|
||||
@@ -304,29 +296,6 @@ public class WebSQLUtils {
|
||||
}
|
||||
}
|
||||
};
|
||||
return webSession.runAsyncTask(task, runnable);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static <T> T requestConfirmation(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull WebAsyncTaskInfo asyncTask,
|
||||
@NotNull WSEvent confirmationEvent,
|
||||
@NotNull CompletableFuture<T> confirmationFuture
|
||||
) throws DBWebException {
|
||||
String attributeName = WebSQLConstants.TASK_CONFIRMATION_ATTR_PREFIX + asyncTask.getId();
|
||||
webSession.setAttribute(attributeName, confirmationFuture);
|
||||
|
||||
webSession.addSessionEvent(confirmationEvent);
|
||||
|
||||
try {
|
||||
return confirmationFuture.get(WebSQLConstants.TASK_CONFIRMATION_TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||
} catch (TimeoutException e) {
|
||||
throw new DBWebException("Query confirmation timeout");
|
||||
} catch (Exception e) {
|
||||
throw new DBWebException("Error when processing confirmation response", e);
|
||||
} finally {
|
||||
webSession.removeAttribute(attributeName);
|
||||
}
|
||||
return webSession.createAndRunAsyncTask("SQL execute", runnable);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -191,8 +191,7 @@ public class WebServiceBindingSQL extends WebServiceBindingBase<DBWServiceSQL>
|
||||
getArgument(env, "resultId"),
|
||||
getDataFilter(env),
|
||||
getDataFormat(env),
|
||||
CommonUtils.toBoolean(getArgument(env, "readLogs")),
|
||||
CommonUtils.toBoolean(getArgument(env, "isInteractive"))
|
||||
CommonUtils.toBoolean(getArgument(env, "readLogs"))
|
||||
)
|
||||
)
|
||||
.dataFetcher("asyncReadDataFromContainer", env ->
|
||||
|
||||
+51
-43
@@ -110,7 +110,7 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public WebSQLDialectInfo getDialectInfo(@NotNull WebConnectionInfo connectionInfo) {
|
||||
public WebSQLDialectInfo getDialectInfo(@NotNull WebConnectionInfo connectionInfo) throws DBWebException {
|
||||
DBPDataSourceContainer dataSourceContainer = connectionInfo.getDataSourceContainer();
|
||||
SQLDialect dialect = getSqlDialectFromConnection(dataSourceContainer);
|
||||
return new WebSQLDialectInfo(dataSourceContainer.getDataSource(), dialect);
|
||||
@@ -400,16 +400,21 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
DBExecUtils.tryExecuteRecover(
|
||||
monitor,
|
||||
contextInfo.getProcessor().getConnection().getDataSource(),
|
||||
monitor1 ->
|
||||
result[0] = contextInfo.getProcessor().updateResultsDataBatch(
|
||||
monitor1, contextInfo, resultsId, updatedRows, deletedRows, addedRows, dataFormat)
|
||||
monitor1 -> {
|
||||
try {
|
||||
result[0] = contextInfo.getProcessor().updateResultsDataBatch(
|
||||
monitor1, contextInfo, resultsId, updatedRows, deletedRows, addedRows, dataFormat);
|
||||
} catch (Exception e) {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
}
|
||||
);
|
||||
return result[0];
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface ThrowableFunction<T, R> {
|
||||
R apply(T obj) throws DBException;
|
||||
R apply(T obj) throws Exception;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -432,6 +437,9 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
@NotNull Integer lobColumnIndex,
|
||||
@NotNull WebSQLResultsRow row
|
||||
) throws DBWebException {
|
||||
if (row == null) {
|
||||
throw new DBWebException("Results row is not found");
|
||||
}
|
||||
WebSQLProcessor processor = contextInfo.getProcessor();
|
||||
ThrowableFunction<DBRProgressMonitor, String> function = monitor -> processor.readStringValue(
|
||||
monitor, contextInfo, resultsId, lobColumnIndex, row);
|
||||
@@ -449,7 +457,13 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
DBExecUtils.tryExecuteRecover(
|
||||
processor.getWebSession().getProgressMonitor(),
|
||||
processor.getConnection().getDataSource(),
|
||||
monitor -> result.append(function.apply(monitor))
|
||||
monitor -> {
|
||||
try {
|
||||
result.append(function.apply(monitor));
|
||||
} catch (Exception e) {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
}
|
||||
);
|
||||
return result.toString();
|
||||
} catch (DBException e) {
|
||||
@@ -462,8 +476,7 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
try {
|
||||
return contextInfo.getProcessor().generateResultsDataUpdateScript(
|
||||
contextInfo.getProcessor().getWebSession().getProgressMonitor(),
|
||||
contextInfo, resultsId, updatedRows, deletedRows, addedRows
|
||||
);
|
||||
contextInfo, resultsId, updatedRows, deletedRows, addedRows, dataFormat);
|
||||
} catch (DBException e) {
|
||||
throw new DBWebException("Error genering update script", e);
|
||||
}
|
||||
@@ -479,8 +492,7 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
@Nullable String resultId,
|
||||
@Nullable WebSQLDataFilter filter,
|
||||
@Nullable WebDataFormat dataFormat,
|
||||
boolean readLogs,
|
||||
boolean useEvents
|
||||
boolean readLogs
|
||||
) throws DBException {
|
||||
if (DBWorkbench.isDistributed() && !webSession.hasPermission(DBWConstants.PERMISSION_SQL_EXECUTE_QUERY)) {
|
||||
throw new DBWebException("Permission denied");
|
||||
@@ -492,8 +504,7 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
resultId,
|
||||
filter,
|
||||
dataFormat,
|
||||
readLogs,
|
||||
useEvents
|
||||
readLogs
|
||||
);
|
||||
}
|
||||
|
||||
@@ -503,11 +514,10 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
@NotNull String nodePath,
|
||||
@Nullable String resultId,
|
||||
@Nullable WebSQLDataFilter filter,
|
||||
@Nullable WebDataFormat dataFormat
|
||||
) {
|
||||
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<>() {
|
||||
@Nullable WebDataFormat dataFormat) throws DBWebException {
|
||||
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<String>() {
|
||||
@Override
|
||||
public void run(DBRProgressMonitor monitor) throws InvocationTargetException {
|
||||
public void run(DBRProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
|
||||
try {
|
||||
monitor.beginTask("Read data", 1);
|
||||
monitor.subTask("Extra data from " + nodePath);
|
||||
@@ -521,8 +531,7 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
dataContainer,
|
||||
resultId,
|
||||
filter != null ? filter : new WebSQLDataFilter(),
|
||||
dataFormat
|
||||
);
|
||||
dataFormat);
|
||||
this.result = executeResults.getStatusMessage();
|
||||
this.extendedResults = executeResults;
|
||||
} catch (Throwable e) {
|
||||
@@ -553,25 +562,24 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
@Override
|
||||
public WebSQLExecuteInfo asyncGetQueryResults(@NotNull WebSession webSession, @NotNull String taskId) throws DBWebException {
|
||||
WebAsyncTaskInfo taskStatus = webSession.asyncTaskStatus(taskId, false);
|
||||
return (WebSQLExecuteInfo) taskStatus.getExtendedResult();
|
||||
if (taskStatus != null) {
|
||||
return (WebSQLExecuteInfo) taskStatus.getExtendedResult();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// Explain plan
|
||||
|
||||
@Override
|
||||
public WebAsyncTaskInfo asyncSqlExplainExecutionPlan(
|
||||
@NotNull WebSQLContextInfo contextInfo,
|
||||
@NotNull String sql,
|
||||
@NotNull Map<String, Object> configuration
|
||||
) {
|
||||
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<>() {
|
||||
public WebAsyncTaskInfo asyncSqlExplainExecutionPlan(@NotNull WebSQLContextInfo contextInfo, @NotNull String sql, @NotNull Map<String, Object> configuration) throws DBException {
|
||||
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<String>() {
|
||||
@Override
|
||||
public void run(DBRProgressMonitor monitor) throws InvocationTargetException {
|
||||
public void run(DBRProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
|
||||
try {
|
||||
monitor.beginTask("Explain execution plan", 1);
|
||||
monitor.subTask("Explain query [" + sql + "] execution plan");
|
||||
WebSQLExecutionPlan executeResults = contextInfo.getProcessor().explainExecutionPlan(monitor, sql, configuration);
|
||||
WebSQLExecutionPlan executeResults = contextInfo.getProcessor().explainExecutionPlan(monitor, contextInfo, sql, configuration);
|
||||
this.result = "Execution plan explain has been scheduled";
|
||||
this.extendedResults = executeResults;
|
||||
} catch (Throwable e) {
|
||||
@@ -587,11 +595,14 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
@Override
|
||||
public WebSQLExecutionPlan asyncSqlExplainExecutionPlanResult(@NotNull WebSession webSession, @NotNull String taskId) throws DBWebException {
|
||||
WebAsyncTaskInfo taskStatus = webSession.asyncTaskStatus(taskId, false);
|
||||
return (WebSQLExecutionPlan) taskStatus.getExtendedResult();
|
||||
if (taskStatus != null) {
|
||||
return (WebSQLExecutionPlan) taskStatus.getExtendedResult();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebSQLScriptInfo parseSqlScript(@NotNull WebConnectionInfo connectionInfo, @NotNull String sqlScript) {
|
||||
public WebSQLScriptInfo parseSqlScript(@NotNull WebConnectionInfo connectionInfo, @NotNull String sqlScript) throws DBWebException {
|
||||
SQLDialect dialect = getSqlDialectFromConnection(connectionInfo.getDataSourceContainer());
|
||||
List<SQLScriptElement> queries = SQLScriptParser.parseScript(
|
||||
connectionInfo.getDataSource(),
|
||||
@@ -605,7 +616,7 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebSQLQueryInfo parseSqlQuery(@NotNull WebConnectionInfo connectionInfo, @NotNull String sqlScript, int cursorPosition) {
|
||||
public WebSQLQueryInfo parseSqlQuery(@NotNull WebConnectionInfo connectionInfo, @NotNull String sqlScript, int cursorPosition) throws DBWebException {
|
||||
SQLDialect dialect = getSqlDialectFromConnection(connectionInfo.getDataSourceContainer());
|
||||
SQLScriptElement query = SQLScriptParser.parseQuery(
|
||||
connectionInfo.getDataSource(),
|
||||
@@ -648,20 +659,14 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebAsyncTaskInfo getRowDataCount(@NotNull WebSession webSession, @NotNull WebSQLContextInfo contextInfo, @NotNull String resultsId) {
|
||||
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<>() {
|
||||
public WebAsyncTaskInfo getRowDataCount(@NotNull WebSession webSession, @NotNull WebSQLContextInfo contextInfo, @NotNull String resultsId) throws DBWebException {
|
||||
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<String>() {
|
||||
@Override
|
||||
public void run(DBRProgressMonitor monitor) throws InvocationTargetException {
|
||||
public void run(DBRProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
|
||||
try {
|
||||
monitor.beginTask("Get row data count", 1);
|
||||
WebSQLResultsInfo results = contextInfo.getResults(resultsId);
|
||||
long rowCount = DBUtils.readRowCount(
|
||||
monitor,
|
||||
contextInfo.getProcessor().getExecutionContext(),
|
||||
results.getDataContainer(),
|
||||
results.getDataFilter(),
|
||||
this
|
||||
);
|
||||
long rowCount = DBUtils.readRowCount(monitor, contextInfo.getProcessor().getExecutionContext(), results.getDataContainer(), null, this);
|
||||
this.result = "Row data count completed";
|
||||
this.extendedResults = rowCount;
|
||||
} catch (Throwable e) {
|
||||
@@ -678,16 +683,19 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
@Nullable
|
||||
public Long getRowDataCountResult(@NotNull WebSession webSession, @NotNull String taskId) throws DBWebException {
|
||||
WebAsyncTaskInfo taskStatus = webSession.asyncTaskStatus(taskId, false);
|
||||
return (Long) taskStatus.getExtendedResult();
|
||||
if (taskStatus != null) {
|
||||
return (Long) taskStatus.getExtendedResult();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebAsyncTaskInfo asyncSqlSetAutoCommit(@NotNull WebSession webSession, @NotNull WebSQLContextInfo contextInfo, boolean autoCommit) {
|
||||
public WebAsyncTaskInfo asyncSqlSetAutoCommit(@NotNull WebSession webSession, @NotNull WebSQLContextInfo contextInfo, boolean autoCommit) throws DBWebException {
|
||||
return contextInfo.setAutoCommit(autoCommit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebAsyncTaskInfo asyncSqlRollbackTransaction(@NotNull WebSession webSession, @NotNull WebSQLContextInfo contextInfo) {
|
||||
public WebAsyncTaskInfo asyncSqlRollbackTransaction(@NotNull WebSession webSession, @NotNull WebSQLContextInfo contextInfo) throws DBWebException {
|
||||
return contextInfo.rollbackTransaction();
|
||||
}
|
||||
|
||||
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.service.sql.messages;
|
||||
|
||||
import org.eclipse.osgi.util.NLS;
|
||||
|
||||
public class WebSQLMessages extends NLS {
|
||||
static final String BUNDLE_NAME = "io.cloudbeaver.service.sql.messages.WebSQLResources"; //$NON-NLS-1$
|
||||
|
||||
// TODO: add localizations
|
||||
public static String model_web_ai_query_confirmation_title;
|
||||
public static String model_web_ai_query_confirmation_message;
|
||||
public static String model_web_dialog_sql_param_title;
|
||||
|
||||
static {
|
||||
NLS.initializeMessages(BUNDLE_NAME, WebSQLMessages.class);
|
||||
}
|
||||
|
||||
private WebSQLMessages() {
|
||||
}
|
||||
|
||||
}
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
model_web_ai_query_confirmation_title = Confirm query execution
|
||||
model_web_ai_query_confirmation_message = The query you're about to execute can modify existing data or schema.\nDo you want to continue?
|
||||
|
||||
model_web_dialog_sql_param_title = Bind parameter(s)
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Service - Administration
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.admin;singleton:=true
|
||||
Bundle-Version: 1.0.135.qualifier
|
||||
Bundle-Release-Date: 20251222
|
||||
Bundle-Version: 1.0.132.qualifier
|
||||
Bundle-Release-Date: 20251103
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: io.cloudbeaver.server.ce,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.admin</artifactId>
|
||||
<version>1.0.135-SNAPSHOT</version>
|
||||
<version>1.0.132-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+6
-3
@@ -16,7 +16,10 @@
|
||||
*/
|
||||
package io.cloudbeaver.service.admin;
|
||||
|
||||
import io.cloudbeaver.*;
|
||||
import io.cloudbeaver.DBWConstants;
|
||||
import io.cloudbeaver.DBWFeatureSet;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebAction;
|
||||
import io.cloudbeaver.model.WebPropertyInfo;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.registry.WebAuthProviderConfiguration;
|
||||
@@ -113,7 +116,7 @@ public interface DBWServiceAdmin extends DBWService {
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull String userID,
|
||||
@NotNull String providerId,
|
||||
@WebParameterSecure @NotNull Map<String, Object> credentials
|
||||
@NotNull Map<String, Object> credentials
|
||||
) throws DBWebException;
|
||||
|
||||
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
|
||||
@@ -153,7 +156,7 @@ public interface DBWServiceAdmin extends DBWService {
|
||||
boolean disabled,
|
||||
@Nullable String iconURL,
|
||||
@Nullable String description,
|
||||
@WebParameterSecure @Nullable Map<String, Object> parameters) throws DBWebException;
|
||||
@Nullable Map<String, Object> parameters) throws DBWebException;
|
||||
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
|
||||
boolean deleteAuthProviderConfiguration(@NotNull WebSession webSession, @NotNull String id) throws DBWebException;
|
||||
|
||||
|
||||
+8
-6
@@ -16,7 +16,10 @@
|
||||
*/
|
||||
package io.cloudbeaver.service.admin.impl;
|
||||
|
||||
import io.cloudbeaver.*;
|
||||
import io.cloudbeaver.DBWFeatureSet;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebProjectImpl;
|
||||
import io.cloudbeaver.WebServiceUtils;
|
||||
import io.cloudbeaver.auth.provider.local.LocalAuthProvider;
|
||||
import io.cloudbeaver.model.WebPropertyInfo;
|
||||
import io.cloudbeaver.model.config.CBAppConfig;
|
||||
@@ -360,11 +363,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setUserCredentials(@NotNull WebSession webSession,
|
||||
@NotNull String userID,
|
||||
@NotNull String providerId,
|
||||
@WebParameterSecure @NotNull Map<String, Object> credentials
|
||||
) throws DBWebException {
|
||||
public boolean setUserCredentials(@NotNull WebSession webSession, @NotNull String userID, @NotNull String providerId, @NotNull Map<String, Object> credentials) throws DBWebException {
|
||||
WebAuthProviderDescriptor authProvider = WebAuthProviderRegistry.getInstance().getAuthProvider(providerId);
|
||||
if (authProvider == null) {
|
||||
throw new DBWebException("Invalid auth provider '" + providerId + "'");
|
||||
@@ -666,6 +665,9 @@ public class WebServiceAdmin implements DBWServiceAdmin {
|
||||
if (configurationMode) {
|
||||
// In config mode we always refresh because admin user doesn't exist yet
|
||||
webSession.resetUserState();
|
||||
} else {
|
||||
// Just reload session state
|
||||
webSession.refreshUserData();
|
||||
}
|
||||
WebAppUtils.getWebApplication().getDriverRegistry().refreshApplicableDrivers();
|
||||
} catch (Throwable e) {
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Service - Authentication
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.auth;singleton:=true
|
||||
Bundle-Version: 1.0.135.qualifier
|
||||
Bundle-Release-Date: 20251222
|
||||
Bundle-Version: 1.0.132.qualifier
|
||||
Bundle-Release-Date: 20251103
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: io.cloudbeaver.server.ce
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.auth</artifactId>
|
||||
<version>1.0.135-SNAPSHOT</version>
|
||||
<version>1.0.132-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+1
-6
@@ -18,7 +18,6 @@ package io.cloudbeaver.service.auth;
|
||||
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebAction;
|
||||
import io.cloudbeaver.WebParameterSecure;
|
||||
import io.cloudbeaver.model.WebPropertyInfo;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.service.DBWService;
|
||||
@@ -78,11 +77,7 @@ public interface DBWServiceAuth extends DBWService {
|
||||
WebAuthProviderInfo[] getAuthProviders(@NotNull HttpServletRequest request) throws DBWebException;
|
||||
|
||||
@WebAction()
|
||||
boolean changeLocalPassword(
|
||||
@NotNull WebSession webSession,
|
||||
@WebParameterSecure @NotNull String oldPassword,
|
||||
@WebParameterSecure @NotNull String newPassword
|
||||
) throws DBWebException;
|
||||
boolean changeLocalPassword(@NotNull WebSession webSession, @NotNull String oldPassword, @NotNull String newPassword) throws DBWebException;
|
||||
|
||||
@WebAction(authRequired = false)
|
||||
WebPropertyInfo[] listUserProfileProperties(@NotNull WebSession webSession);
|
||||
|
||||
+2
-8
@@ -46,9 +46,8 @@ public class WebAsyncAuthJob extends AbstractJob implements CustomCancelableJob
|
||||
}
|
||||
|
||||
//do nothing, this job is workaround to use exist async process
|
||||
@NotNull
|
||||
@Override
|
||||
protected IStatus run(@NotNull DBRProgressMonitor monitor) {
|
||||
protected IStatus run(DBRProgressMonitor monitor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -72,13 +71,8 @@ public class WebAsyncAuthJob extends AbstractJob implements CustomCancelableJob
|
||||
|
||||
@Override
|
||||
public void cancelJob(@NotNull WebSession webSession, @NotNull WebAsyncTaskInfo taskInfo) {
|
||||
cancelJob(webSession, taskInfo, "Canceled by the user");
|
||||
}
|
||||
|
||||
public void cancelJob(@NotNull WebSession webSession, @NotNull WebAsyncTaskInfo taskInfo, @NotNull String errorMessage) {
|
||||
taskInfo.setRunning(false);
|
||||
taskInfo.setJobError(new DBException(errorMessage));
|
||||
taskInfo.setJobError(new DBException("Canceled by the user"));
|
||||
WebEventUtils.sendAsyncTaskEvent(webSession, taskInfo);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.service.auth;
|
||||
|
||||
import io.cloudbeaver.model.WebAsyncTaskInfo;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.core.runtime.jobs.Job;
|
||||
import org.jkiss.code.NotNull;
|
||||
|
||||
public class WebAsyncAuthTimeoutJob extends Job {
|
||||
@NotNull
|
||||
private final WebSession webSession;
|
||||
@NotNull
|
||||
private final WebAsyncTaskInfo asyncInfo;
|
||||
@NotNull
|
||||
private final WebAsyncAuthJob authJob;
|
||||
|
||||
public WebAsyncAuthTimeoutJob(@NotNull WebSession webSession, @NotNull WebAsyncTaskInfo asyncInfo, @NotNull WebAsyncAuthJob authJob) {
|
||||
super("Auth timeout job for " + asyncInfo.getName());
|
||||
this.webSession = webSession;
|
||||
this.asyncInfo = asyncInfo;
|
||||
this.authJob = authJob;
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected IStatus run(@NotNull IProgressMonitor monitor) {
|
||||
if (asyncInfo.isRunning()) {
|
||||
authJob.cancelJob(webSession, asyncInfo, "Authentication timeout exceeded");
|
||||
}
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user