fix: fix MySQL stale connection error (#24429)
## Description - Add changes to address the`StaleConnection` exception caused by MySQL plugin. - Update MySQL driver version. - Other refactor changes not related to the main issue: - Explicit empty constructor definition is replaced with Lombok annotation for all error messages class. - A base class is created for plugin error messages class to store all common error messages. - Fix Indentation.
This commit is contained in:
parent
4ce12be224
commit
98a509227f
|
|
@ -11,4 +11,24 @@ public interface PluginConstants {
|
||||||
String AMAZON_S3_PLUGIN = "amazons3-plugin";
|
String AMAZON_S3_PLUGIN = "amazons3-plugin";
|
||||||
String GOOGLE_SHEETS_PLUGIN = "google-sheets-plugin";
|
String GOOGLE_SHEETS_PLUGIN = "google-sheets-plugin";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface PluginName {
|
||||||
|
public static final String S3_PLUGIN_NAME = "S3";
|
||||||
|
public static final String ARANGO_PLUGIN_NAME = "Arango";
|
||||||
|
public static final String DYNAMO_PLUGIN_NAME = "Dynamo";
|
||||||
|
public static final String ELASTIC_SEARCH_PLUGIN_NAME = "ElasticSearch";
|
||||||
|
public static final String FIRESTORE_PLUGIN_NAME = "Firestore";
|
||||||
|
public static final String GOOGLE_SHEETS_PLUGIN_NAME = "GoogleSheets";
|
||||||
|
public static final String GRAPHQL_PLUGIN_NAME = "Graphql";
|
||||||
|
public static final String MSSQL_PLUGIN_NAME = "Mssql";
|
||||||
|
public static final String MYSQL_PLUGIN_NAME = "Mysql";
|
||||||
|
public static final String ORACLE_PLUGIN_NAME = "Oracle";
|
||||||
|
public static final String POSTGRES_PLUGIN_NAME = "Postgres";
|
||||||
|
public static final String REDIS_PLUGIN_NAME = "Redis";
|
||||||
|
public static final String REDSHIFT_PLUGIN_NAME = "Redshift";
|
||||||
|
public static final String REST_API_PLUGIN_NAME = "RestApi";
|
||||||
|
public static final String SAAS_PLUGIN_NAME = "Saas";
|
||||||
|
public static final String SMTP_PLUGIN_NAME = "Smtp";
|
||||||
|
public static final String SNOWFLAKE_PLUGIN_NAME = "Snowflake";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.appsmith.external.exceptions.pluginExceptions;
|
||||||
|
|
||||||
|
public abstract class BasePluginErrorMessages {
|
||||||
|
public static final String CONNECTION_INVALID_ERROR_MSG = "Connection object is invalid.";
|
||||||
|
public static final String CONNECTION_NULL_ERROR_MSG = "Connection object is null.";
|
||||||
|
public static final String CONNECTION_CLOSED_ERROR_MSG = "Connection object is closed.";
|
||||||
|
public static final String CONNECTION_POOL_NULL_ERROR_MSG = "Connection pool is null.";
|
||||||
|
public static final String CONNECTION_POOL_CLOSED_ERROR_MSG = "Connection pool is closed.";
|
||||||
|
public static final String CONNECTION_POOL_NOT_RUNNING_ERROR_MSG = "Connection pool is not running.";
|
||||||
|
public static final String UNKNOWN_CONNECTION_ERROR_MSG = "Unknown connection error. Please reach out to Appsmith " +
|
||||||
|
"customer support to resolve this.";
|
||||||
|
public static final String JDBC_DRIVER_LOADING_ERROR_MSG = "Error loading JDBC Driver class.";
|
||||||
|
}
|
||||||
|
|
@ -1,14 +1,22 @@
|
||||||
package com.appsmith.external.exceptions.pluginExceptions;
|
package com.appsmith.external.exceptions.pluginExceptions;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
public class StaleConnectionException extends RuntimeException {
|
public class StaleConnectionException extends RuntimeException {
|
||||||
public StaleConnectionException() {
|
String message = "";
|
||||||
}
|
|
||||||
|
|
||||||
public StaleConnectionException(String message) {
|
public StaleConnectionException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
|
this.message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public StaleConnectionException(String message, Throwable cause) {
|
public StaleConnectionException(String message, Throwable cause) {
|
||||||
super(message, cause);
|
super(message, cause);
|
||||||
|
this.message = message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,7 @@ import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
|
||||||
import com.appsmith.external.models.Condition;
|
import com.appsmith.external.models.Condition;
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
|
||||||
|
|
@ -71,10 +71,10 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
|
|
||||||
import static com.appsmith.external.constants.ActionConstants.ACTION_CONFIGURATION_BODY;
|
import static com.appsmith.external.constants.ActionConstants.ACTION_CONFIGURATION_BODY;
|
||||||
import static com.appsmith.external.constants.ActionConstants.ACTION_CONFIGURATION_PATH;
|
import static com.appsmith.external.constants.ActionConstants.ACTION_CONFIGURATION_PATH;
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.CONNECTION_NULL_ERROR_MSG;
|
||||||
import static com.appsmith.external.helpers.PluginUtils.OBJECT_TYPE;
|
import static com.appsmith.external.helpers.PluginUtils.OBJECT_TYPE;
|
||||||
import static com.appsmith.external.helpers.PluginUtils.STRING_TYPE;
|
import static com.appsmith.external.helpers.PluginUtils.STRING_TYPE;
|
||||||
import static com.appsmith.external.helpers.PluginUtils.getDataValueSafelyFromFormData;
|
import static com.appsmith.external.helpers.PluginUtils.getDataValueSafelyFromFormData;
|
||||||
|
|
@ -459,7 +459,7 @@ public class AmazonS3Plugin extends BasePlugin {
|
||||||
* - If connection object is null, then assume stale connection.
|
* - If connection object is null, then assume stale connection.
|
||||||
*/
|
*/
|
||||||
if (connection == null) {
|
if (connection == null) {
|
||||||
return Mono.error(new StaleConnectionException());
|
return Mono.error(new StaleConnectionException(CONNECTION_NULL_ERROR_MSG));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actionConfiguration == null) {
|
if (actionConfiguration == null) {
|
||||||
|
|
@ -787,7 +787,7 @@ public class AmazonS3Plugin extends BasePlugin {
|
||||||
}
|
}
|
||||||
return Mono.just(actionResult);
|
return Mono.just(actionResult);
|
||||||
})
|
})
|
||||||
.onErrorMap(IllegalStateException.class, error -> new StaleConnectionException())
|
.onErrorMap(IllegalStateException.class, error -> new StaleConnectionException(error.getMessage()))
|
||||||
.flatMap(obj -> obj)
|
.flatMap(obj -> obj)
|
||||||
.flatMap(result -> {
|
.flatMap(result -> {
|
||||||
ActionExecutionResult actionExecutionResult = new ActionExecutionResult();
|
ActionExecutionResult actionExecutionResult = new ActionExecutionResult();
|
||||||
|
|
@ -986,8 +986,8 @@ public class AmazonS3Plugin extends BasePlugin {
|
||||||
S3ErrorMessages.LIST_OF_BUCKET_FETCHING_ERROR_MSG,
|
S3ErrorMessages.LIST_OF_BUCKET_FETCHING_ERROR_MSG,
|
||||||
e.getMessage()
|
e.getMessage()
|
||||||
);
|
);
|
||||||
} catch (IllegalStateException s) {
|
} catch (IllegalStateException e) {
|
||||||
throw new StaleConnectionException();
|
throw new StaleConnectionException(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DatasourceStructure(tableList);
|
return new DatasourceStructure(tableList);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
package com.external.plugins.exceptions;
|
package com.external.plugins.exceptions;
|
||||||
|
|
||||||
public class S3ErrorMessages {
|
import com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages;
|
||||||
private S3ErrorMessages() {
|
import lombok.AccessLevel;
|
||||||
//Prevents instantiation
|
import lombok.NoArgsConstructor;
|
||||||
}
|
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE) // To prevent instantiation
|
||||||
|
public class S3ErrorMessages extends BasePluginErrorMessages {
|
||||||
public static final String FILE_CONTENT_FETCHING_ERROR_MSG = "Appsmith server has encountered an unexpected error when fetching file " +
|
public static final String FILE_CONTENT_FETCHING_ERROR_MSG = "Appsmith server has encountered an unexpected error when fetching file " +
|
||||||
"content from AWS S3 server. Please reach out to Appsmith customer support to resolve this.";
|
"content from AWS S3 server. Please reach out to Appsmith customer support to resolve this.";
|
||||||
|
|
||||||
|
|
@ -99,6 +101,4 @@ public class S3ErrorMessages {
|
||||||
public static final String DS_MANDATORY_PARAMETER_ENDPOINT_URL_MISSING_ERROR_MSG = "Required parameter 'Endpoint URL' is empty. Did you forget to edit the 'Endpoint" +
|
public static final String DS_MANDATORY_PARAMETER_ENDPOINT_URL_MISSING_ERROR_MSG = "Required parameter 'Endpoint URL' is empty. Did you forget to edit the 'Endpoint" +
|
||||||
" URL' field in the datasource creation form ? You need to fill it with " +
|
" URL' field in the datasource creation form ? You need to fill it with " +
|
||||||
"the endpoint URL of your S3 instance.";
|
"the endpoint URL of your S3 instance.";
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ import com.arangodb.model.CollectionsReadOptions;
|
||||||
import com.external.plugins.exceptions.ArangoDBErrorMessages;
|
import com.external.plugins.exceptions.ArangoDBErrorMessages;
|
||||||
import com.external.plugins.exceptions.ArangoDBPluginError;
|
import com.external.plugins.exceptions.ArangoDBPluginError;
|
||||||
import com.external.utils.ArangoDBErrorUtils;
|
import com.external.utils.ArangoDBErrorUtils;
|
||||||
import java.util.Properties;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang.ObjectUtils;
|
import org.apache.commons.lang.ObjectUtils;
|
||||||
import org.pf4j.Extension;
|
import org.pf4j.Extension;
|
||||||
|
|
@ -51,6 +50,7 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static com.appsmith.external.constants.ActionConstants.ACTION_CONFIGURATION_BODY;
|
import static com.appsmith.external.constants.ActionConstants.ACTION_CONFIGURATION_BODY;
|
||||||
import static com.appsmith.external.helpers.PluginUtils.MATCH_QUOTED_WORDS_REGEX;
|
import static com.appsmith.external.helpers.PluginUtils.MATCH_QUOTED_WORDS_REGEX;
|
||||||
|
import static com.external.plugins.exceptions.ArangoDBErrorMessages.CONNECTION_INVALID_ERROR_MSG;
|
||||||
import static com.external.plugins.exceptions.ArangoDBErrorMessages.DS_HOSTNAME_MISSING_OR_INVALID_ERROR_MSG;
|
import static com.external.plugins.exceptions.ArangoDBErrorMessages.DS_HOSTNAME_MISSING_OR_INVALID_ERROR_MSG;
|
||||||
import static com.external.utils.SSLUtils.isCaCertificateAvailable;
|
import static com.external.utils.SSLUtils.isCaCertificateAvailable;
|
||||||
import static com.external.utils.SSLUtils.setSSLContext;
|
import static com.external.utils.SSLUtils.setSSLContext;
|
||||||
|
|
@ -84,7 +84,7 @@ public class ArangoDBPlugin extends BasePlugin {
|
||||||
ActionConfiguration actionConfiguration) {
|
ActionConfiguration actionConfiguration) {
|
||||||
|
|
||||||
if (!isConnectionValid(db)) {
|
if (!isConnectionValid(db)) {
|
||||||
return Mono.error(new StaleConnectionException());
|
return Mono.error(new StaleConnectionException(CONNECTION_INVALID_ERROR_MSG));
|
||||||
}
|
}
|
||||||
|
|
||||||
String query = actionConfiguration.getBody();
|
String query = actionConfiguration.getBody();
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
package com.external.plugins.exceptions;
|
package com.external.plugins.exceptions;
|
||||||
|
|
||||||
public class ArangoDBErrorMessages {
|
import com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages;
|
||||||
private ArangoDBErrorMessages() {
|
import lombok.AccessLevel;
|
||||||
//Prevents instantiation
|
import lombok.NoArgsConstructor;
|
||||||
}
|
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE) // To prevent instantiation
|
||||||
|
public class ArangoDBErrorMessages extends BasePluginErrorMessages {
|
||||||
public static final String MISSING_QUERY_ERROR_MSG = "Missing required parameter: Query.";
|
public static final String MISSING_QUERY_ERROR_MSG = "Missing required parameter: Query.";
|
||||||
|
|
||||||
public static final String QUERY_EXECUTION_FAILED_ERROR_MSG = "Your query failed to execute. Please check more information in the error details.";
|
public static final String QUERY_EXECUTION_FAILED_ERROR_MSG = "Your query failed to execute. Please check more information in the error details.";
|
||||||
|
|
@ -20,7 +22,6 @@ public class ArangoDBErrorMessages {
|
||||||
public static final String GET_STRUCTURE_ERROR_MSG = "Appsmith server has failed to fetch list of collections from database. Please check " +
|
public static final String GET_STRUCTURE_ERROR_MSG = "Appsmith server has failed to fetch list of collections from database. Please check " +
|
||||||
"if the database credentials are valid and/or you have the required permissions.";
|
"if the database credentials are valid and/or you have the required permissions.";
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
************************************************************************************************************************************************
|
************************************************************************************************************************************************
|
||||||
Error messages related to validation of datasource.
|
Error messages related to validation of datasource.
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
package com.external.plugins.exceptions;
|
package com.external.plugins.exceptions;
|
||||||
|
|
||||||
public class DynamoErrorMessages {
|
import com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages;
|
||||||
private DynamoErrorMessages() {
|
import lombok.AccessLevel;
|
||||||
//Prevents instantiation
|
import lombok.NoArgsConstructor;
|
||||||
}
|
|
||||||
|
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE) // To prevent instantiation
|
||||||
|
public class DynamoErrorMessages extends BasePluginErrorMessages {
|
||||||
public static final String MISSING_ACTION_NAME_ERROR_MSG = "Missing action name (like `ListTables`, `GetItem` etc.).";
|
public static final String MISSING_ACTION_NAME_ERROR_MSG = "Missing action name (like `ListTables`, `GetItem` etc.).";
|
||||||
|
|
||||||
public static final String UNKNOWN_ACTION_NAME_ERROR_MSG = "Unknown action: `%s`. Note that action names are case-sensitive.";
|
public static final String UNKNOWN_ACTION_NAME_ERROR_MSG = "Unknown action: `%s`. Note that action names are case-sensitive.";
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
package com.external.plugins.exceptions;
|
package com.external.plugins.exceptions;
|
||||||
|
|
||||||
public class ElasticSearchErrorMessages {
|
import com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages;
|
||||||
private ElasticSearchErrorMessages() {
|
import lombok.AccessLevel;
|
||||||
//Prevents instantiation
|
import lombok.NoArgsConstructor;
|
||||||
}
|
|
||||||
|
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE) // To prevent instantiation
|
||||||
|
public class ElasticSearchErrorMessages extends BasePluginErrorMessages {
|
||||||
public static final String ARRAY_TO_ND_JSON_ARRAY_CONVERSION_ERROR_MSG = "Error occurred while converting array to ND-JSON";
|
public static final String ARRAY_TO_ND_JSON_ARRAY_CONVERSION_ERROR_MSG = "Error occurred while converting array to ND-JSON";
|
||||||
|
|
||||||
public static final String QUERY_EXECUTION_FAILED_ERROR_MSG = "Error occurred while executing Elasticsearch query.";
|
public static final String QUERY_EXECUTION_FAILED_ERROR_MSG = "Error occurred while executing Elasticsearch query.";
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
package com.external.plugins.exceptions;
|
package com.external.plugins.exceptions;
|
||||||
|
|
||||||
public class FirestoreErrorMessages {
|
import com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages;
|
||||||
private FirestoreErrorMessages() {
|
import lombok.AccessLevel;
|
||||||
//Prevents instantiation
|
import lombok.NoArgsConstructor;
|
||||||
}
|
|
||||||
|
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE) // To prevent instantiation
|
||||||
|
public class FirestoreErrorMessages extends BasePluginErrorMessages {
|
||||||
public static final String MANDATORY_PARAM_COMMAND_MISSING_ERROR_MSG = "Mandatory parameter 'Command' is missing. Did you forget to select one of the commands" +
|
public static final String MANDATORY_PARAM_COMMAND_MISSING_ERROR_MSG = "Mandatory parameter 'Command' is missing. Did you forget to select one of the commands" +
|
||||||
" from the Command dropdown ?";
|
" from the Command dropdown ?";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
package com.external.constants;
|
package com.external.constants;
|
||||||
|
|
||||||
public class ErrorMessages {
|
import com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE) // To prevent instantiation
|
||||||
|
public class ErrorMessages extends BasePluginErrorMessages {
|
||||||
|
|
||||||
public static final String EMPTY_ROW_OBJECT_MESSAGE = "Row object(s) cannot be empty.";
|
public static final String EMPTY_ROW_OBJECT_MESSAGE = "Row object(s) cannot be empty.";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
package com.external.plugins.exceptions;
|
package com.external.plugins.exceptions;
|
||||||
|
|
||||||
public class GraphQLErrorMessages {
|
import com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages;
|
||||||
private GraphQLErrorMessages() {
|
import lombok.AccessLevel;
|
||||||
//Prevents instantiation
|
import lombok.NoArgsConstructor;
|
||||||
}
|
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE) // To prevent instantiation
|
||||||
|
public class GraphQLErrorMessages extends BasePluginErrorMessages {
|
||||||
public static final String URI_SYNTAX_WRONG_ERROR_MSG = "URI is invalid. Please rectify the URI and try again.";
|
public static final String URI_SYNTAX_WRONG_ERROR_MSG = "URI is invalid. Please rectify the URI and try again.";
|
||||||
public static final String INVALID_CONTENT_TYPE_ERROR_MSG = "Invalid value for Content-Type.";
|
public static final String INVALID_CONTENT_TYPE_ERROR_MSG = "Invalid value for Content-Type.";
|
||||||
public static final String NO_HTTP_METHOD_ERROR_MSG = "HTTPMethod must be set.";
|
public static final String NO_HTTP_METHOD_ERROR_MSG = "HTTPMethod must be set.";
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,7 @@ import static com.external.plugins.constants.FieldName.SMART_SUBSTITUTION;
|
||||||
import static com.external.plugins.constants.FieldName.SUCCESS;
|
import static com.external.plugins.constants.FieldName.SUCCESS;
|
||||||
import static com.external.plugins.constants.FieldName.UPDATE_OPERATION;
|
import static com.external.plugins.constants.FieldName.UPDATE_OPERATION;
|
||||||
import static com.external.plugins.constants.FieldName.UPDATE_QUERY;
|
import static com.external.plugins.constants.FieldName.UPDATE_QUERY;
|
||||||
|
import static com.external.plugins.exceptions.MongoPluginErrorMessages.MONGO_CLIENT_NULL_ERROR_MSG;
|
||||||
import static com.external.plugins.utils.DatasourceUtils.KEY_PASSWORD;
|
import static com.external.plugins.utils.DatasourceUtils.KEY_PASSWORD;
|
||||||
import static com.external.plugins.utils.DatasourceUtils.KEY_URI_DEFAULT_DBNAME;
|
import static com.external.plugins.utils.DatasourceUtils.KEY_URI_DEFAULT_DBNAME;
|
||||||
import static com.external.plugins.utils.DatasourceUtils.KEY_USERNAME;
|
import static com.external.plugins.utils.DatasourceUtils.KEY_USERNAME;
|
||||||
|
|
@ -134,7 +135,6 @@ import static com.external.plugins.utils.MongoPluginUtils.isRawCommand;
|
||||||
import static java.lang.Boolean.TRUE;
|
import static java.lang.Boolean.TRUE;
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static org.apache.logging.log4j.util.Strings.isBlank;
|
import static org.apache.logging.log4j.util.Strings.isBlank;
|
||||||
import static org.apache.logging.log4j.util.Strings.isEmpty;
|
|
||||||
|
|
||||||
public class MongoPlugin extends BasePlugin {
|
public class MongoPlugin extends BasePlugin {
|
||||||
|
|
||||||
|
|
@ -310,7 +310,7 @@ public class MongoPlugin extends BasePlugin {
|
||||||
|
|
||||||
if (mongoClient == null) {
|
if (mongoClient == null) {
|
||||||
log.info("Encountered null connection in MongoDB plugin. Reporting back.");
|
log.info("Encountered null connection in MongoDB plugin. Reporting back.");
|
||||||
throw new StaleConnectionException();
|
throw new StaleConnectionException(MONGO_CLIENT_NULL_ERROR_MSG);
|
||||||
}
|
}
|
||||||
Mono<Document> mongoOutputMono;
|
Mono<Document> mongoOutputMono;
|
||||||
ActionExecutionResult result = new ActionExecutionResult();
|
ActionExecutionResult result = new ActionExecutionResult();
|
||||||
|
|
@ -354,13 +354,13 @@ public class MongoPlugin extends BasePlugin {
|
||||||
*/
|
*/
|
||||||
.onErrorMap(
|
.onErrorMap(
|
||||||
IllegalStateException.class,
|
IllegalStateException.class,
|
||||||
error -> new StaleConnectionException()
|
error -> new StaleConnectionException(error.getMessage())
|
||||||
)
|
)
|
||||||
// This is an experimental fix to handle the scenario where after a period of inactivity, the mongo
|
// This is an experimental fix to handle the scenario where after a period of inactivity, the mongo
|
||||||
// database drops the connection which makes the client throw the following exception.
|
// database drops the connection which makes the client throw the following exception.
|
||||||
.onErrorMap(
|
.onErrorMap(
|
||||||
MongoSocketWriteException.class,
|
MongoSocketWriteException.class,
|
||||||
error -> new StaleConnectionException()
|
error -> new StaleConnectionException(error.getMessage())
|
||||||
)
|
)
|
||||||
.flatMap(mongoOutput -> {
|
.flatMap(mongoOutput -> {
|
||||||
try {
|
try {
|
||||||
|
|
@ -917,13 +917,13 @@ public class MongoPlugin extends BasePlugin {
|
||||||
*/
|
*/
|
||||||
.onErrorMap(
|
.onErrorMap(
|
||||||
IllegalStateException.class,
|
IllegalStateException.class,
|
||||||
error -> new StaleConnectionException()
|
error -> new StaleConnectionException(error.getMessage())
|
||||||
)
|
)
|
||||||
// This is an experimental fix to handle the scenario where after a period of inactivity, the mongo
|
// This is an experimental fix to handle the scenario where after a period of inactivity, the mongo
|
||||||
// database drops the connection which makes the client throw the following exception.
|
// database drops the connection which makes the client throw the following exception.
|
||||||
.onErrorMap(
|
.onErrorMap(
|
||||||
MongoSocketWriteException.class,
|
MongoSocketWriteException.class,
|
||||||
error -> new StaleConnectionException()
|
error -> new StaleConnectionException(error.getMessage())
|
||||||
)
|
)
|
||||||
.onErrorMap(
|
.onErrorMap(
|
||||||
MongoCommandException.class,
|
MongoCommandException.class,
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@ public class MongoPluginErrorMessages {
|
||||||
|
|
||||||
public static final String QUERY_INVALID_ERROR_MSG = "Your query is invalid";
|
public static final String QUERY_INVALID_ERROR_MSG = "Your query is invalid";
|
||||||
|
|
||||||
|
public static final String MONGO_CLIENT_NULL_ERROR_MSG = "Mongo client object is null.";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
************************************************************************************************************************************************
|
************************************************************************************************************************************************
|
||||||
Error messages related to validation of datasource.
|
Error messages related to validation of datasource.
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@
|
||||||
<groupId>com.zaxxer</groupId>
|
<groupId>com.zaxxer</groupId>
|
||||||
<artifactId>HikariCP</artifactId>
|
<artifactId>HikariCP</artifactId>
|
||||||
<version>5.0.1</version>
|
<version>5.0.1</version>
|
||||||
<scope>compile</scope>
|
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
|
|
|
||||||
|
|
@ -66,10 +66,13 @@ import java.util.Set;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
import static com.appsmith.external.constants.ActionConstants.ACTION_CONFIGURATION_BODY;
|
import static com.appsmith.external.constants.ActionConstants.ACTION_CONFIGURATION_BODY;
|
||||||
|
import static com.appsmith.external.constants.PluginConstants.PluginName.MSSQL_PLUGIN_NAME;
|
||||||
import static com.appsmith.external.helpers.PluginUtils.getIdenticalColumns;
|
import static com.appsmith.external.helpers.PluginUtils.getIdenticalColumns;
|
||||||
import static com.appsmith.external.helpers.PluginUtils.getPSParamLabel;
|
import static com.appsmith.external.helpers.PluginUtils.getPSParamLabel;
|
||||||
import static com.appsmith.external.helpers.SmartSubstitutionHelper.replaceQuestionMarkWithDollarIndex;
|
import static com.appsmith.external.helpers.SmartSubstitutionHelper.replaceQuestionMarkWithDollarIndex;
|
||||||
import static com.external.plugins.utils.MssqlDatasourceUtils.getConnectionFromConnectionPool;
|
import static com.external.plugins.exceptions.MssqlErrorMessages.CONNECTION_CLOSED_ERROR_MSG;
|
||||||
|
import static com.external.plugins.exceptions.MssqlErrorMessages.CONNECTION_INVALID_ERROR_MSG;
|
||||||
|
import static com.external.plugins.exceptions.MssqlErrorMessages.CONNECTION_NULL_ERROR_MSG;
|
||||||
import static com.external.plugins.utils.MssqlDatasourceUtils.logHikariCPStatus;
|
import static com.external.plugins.utils.MssqlDatasourceUtils.logHikariCPStatus;
|
||||||
import static com.external.plugins.utils.MssqlExecuteUtils.closeConnectionPostExecution;
|
import static com.external.plugins.utils.MssqlExecuteUtils.closeConnectionPostExecution;
|
||||||
import static java.lang.Boolean.FALSE;
|
import static java.lang.Boolean.FALSE;
|
||||||
|
|
@ -90,6 +93,8 @@ public class MssqlPlugin extends BasePlugin {
|
||||||
|
|
||||||
private static final long MS_SQL_DEFAULT_PORT = 1433L;
|
private static final long MS_SQL_DEFAULT_PORT = 1433L;
|
||||||
|
|
||||||
|
public static final MssqlDatasourceUtils mssqlDatasourceUtils = new MssqlDatasourceUtils();
|
||||||
|
|
||||||
public MssqlPlugin(PluginWrapper wrapper) {
|
public MssqlPlugin(PluginWrapper wrapper) {
|
||||||
super(wrapper);
|
super(wrapper);
|
||||||
}
|
}
|
||||||
|
|
@ -190,19 +195,36 @@ public class MssqlPlugin extends BasePlugin {
|
||||||
final List<String> columnsList = new ArrayList<>();
|
final List<String> columnsList = new ArrayList<>();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
sqlConnectionFromPool = getConnectionFromConnectionPool(hikariDSConnection);
|
sqlConnectionFromPool =
|
||||||
|
mssqlDatasourceUtils.getConnectionFromHikariConnectionPool(hikariDSConnection,
|
||||||
|
MSSQL_PLUGIN_NAME);
|
||||||
} catch (SQLException | StaleConnectionException e) {
|
} catch (SQLException | StaleConnectionException e) {
|
||||||
// The function can throw either StaleConnectionException or SQLException. The underlying hikari
|
// The function can throw either StaleConnectionException or SQLException. The underlying hikari
|
||||||
// library throws SQLException in case the pool is closed or there is an issue initializing
|
// library throws SQLException in case the pool is closed or there is an issue initializing
|
||||||
// the connection pool which can also be translated in our world to StaleConnectionException
|
// the connection pool which can also be translated in our world to StaleConnectionException
|
||||||
// and should then trigger the destruction and recreation of the pool.
|
// and should then trigger the destruction and recreation of the pool.
|
||||||
return Mono.error(e instanceof StaleConnectionException ? e : new StaleConnectionException());
|
return Mono.error(e instanceof StaleConnectionException ? e :
|
||||||
|
new StaleConnectionException(e.getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (sqlConnectionFromPool == null || sqlConnectionFromPool.isClosed() || !sqlConnectionFromPool.isValid(VALIDITY_CHECK_TIMEOUT)) {
|
if (sqlConnectionFromPool == null || sqlConnectionFromPool.isClosed() || !sqlConnectionFromPool.isValid(VALIDITY_CHECK_TIMEOUT)) {
|
||||||
log.info("Encountered stale connection in MsSQL plugin. Reporting back.");
|
log.info("Encountered stale connection in MsSQL plugin. Reporting back.");
|
||||||
return Mono.error(new StaleConnectionException());
|
|
||||||
|
if (sqlConnectionFromPool == null) {
|
||||||
|
return Mono.error(new StaleConnectionException(CONNECTION_NULL_ERROR_MSG));
|
||||||
|
}
|
||||||
|
else if (sqlConnectionFromPool.isClosed()) {
|
||||||
|
return Mono.error(new StaleConnectionException(CONNECTION_CLOSED_ERROR_MSG));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/**
|
||||||
|
* Not adding explicit `!sqlConnectionFromPool.isValid(VALIDITY_CHECK_TIMEOUT)`
|
||||||
|
* check here because this check may take few seconds to complete hence adding
|
||||||
|
* extra time delay.
|
||||||
|
*/
|
||||||
|
return Mono.error(new StaleConnectionException(CONNECTION_INVALID_ERROR_MSG));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (SQLException error) {
|
} catch (SQLException error) {
|
||||||
// This exception is thrown only when the timeout to `isValid` is negative. Since, that's not the case,
|
// This exception is thrown only when the timeout to `isValid` is negative. Since, that's not the case,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
package com.external.plugins.exceptions;
|
package com.external.plugins.exceptions;
|
||||||
|
|
||||||
public class MssqlErrorMessages {
|
import com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
private MssqlErrorMessages() {
|
@NoArgsConstructor(access = AccessLevel.PRIVATE) // To prevent instantiation
|
||||||
//Prevents instantiation
|
public class MssqlErrorMessages extends BasePluginErrorMessages {
|
||||||
}
|
|
||||||
public static final String MISSING_QUERY_ERROR_MSG = "Missing required parameter: Query.";
|
public static final String MISSING_QUERY_ERROR_MSG = "Missing required parameter: Query.";
|
||||||
|
|
||||||
public static final String QUERY_EXECUTION_FAILED_ERROR_MSG = "Your query failed to execute. Please check more information in the error details.";
|
public static final String QUERY_EXECUTION_FAILED_ERROR_MSG = "Your query failed to execute. Please check more information in the error details.";
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,6 @@ import com.appsmith.external.models.DatasourceStructure;
|
||||||
import com.external.plugins.exceptions.MssqlErrorMessages;
|
import com.external.plugins.exceptions.MssqlErrorMessages;
|
||||||
import com.zaxxer.hikari.HikariDataSource;
|
import com.zaxxer.hikari.HikariDataSource;
|
||||||
import com.zaxxer.hikari.HikariPoolMXBean;
|
import com.zaxxer.hikari.HikariPoolMXBean;
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
|
@ -24,11 +22,16 @@ import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static com.appsmith.external.constants.PluginConstants.PluginName.MSSQL_PLUGIN_NAME;
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.CONNECTION_POOL_CLOSED_ERROR_MSG;
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.CONNECTION_POOL_NOT_RUNNING_ERROR_MSG;
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.CONNECTION_POOL_NULL_ERROR_MSG;
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.UNKNOWN_CONNECTION_ERROR_MSG;
|
||||||
import static com.appsmith.external.helpers.PluginUtils.safelyCloseSingleConnectionFromHikariCP;
|
import static com.appsmith.external.helpers.PluginUtils.safelyCloseSingleConnectionFromHikariCP;
|
||||||
import static com.external.plugins.MssqlPlugin.MssqlPluginExecutor.scheduler;
|
import static com.external.plugins.MssqlPlugin.MssqlPluginExecutor.scheduler;
|
||||||
|
import static com.external.plugins.MssqlPlugin.mssqlDatasourceUtils;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
|
||||||
public class MssqlDatasourceUtils {
|
public class MssqlDatasourceUtils {
|
||||||
|
|
||||||
public static final String PRIMARY_KEY_INDICATOR = "PRIMARY KEY";
|
public static final String PRIMARY_KEY_INDICATOR = "PRIMARY KEY";
|
||||||
|
|
@ -94,13 +97,15 @@ public class MssqlDatasourceUtils {
|
||||||
return Mono.fromSupplier(() -> {
|
return Mono.fromSupplier(() -> {
|
||||||
Connection connectionFromPool;
|
Connection connectionFromPool;
|
||||||
try {
|
try {
|
||||||
connectionFromPool = getConnectionFromConnectionPool(connection);
|
connectionFromPool = mssqlDatasourceUtils.getConnectionFromHikariConnectionPool(connection,
|
||||||
|
MSSQL_PLUGIN_NAME);
|
||||||
} catch (SQLException | StaleConnectionException e) {
|
} catch (SQLException | StaleConnectionException e) {
|
||||||
// The function can throw either StaleConnectionException or SQLException. The
|
// The function can throw either StaleConnectionException or SQLException. The
|
||||||
// underlying hikari library throws SQLException in case the pool is closed or there is an issue
|
// underlying hikari library throws SQLException in case the pool is closed or there is an issue
|
||||||
// initializing the connection pool which can also be translated in our world to
|
// initializing the connection pool which can also be translated in our world to
|
||||||
// StaleConnectionException and should then trigger the destruction and recreation of the pool.
|
// StaleConnectionException and should then trigger the destruction and recreation of the pool.
|
||||||
return Mono.error(e instanceof StaleConnectionException ? e : new StaleConnectionException());
|
return Mono.error(e instanceof StaleConnectionException ? e :
|
||||||
|
new StaleConnectionException(e.getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
logHikariCPStatus("Before getting Mssql DB schema", connection);
|
logHikariCPStatus("Before getting Mssql DB schema", connection);
|
||||||
|
|
@ -137,21 +142,9 @@ public class MssqlDatasourceUtils {
|
||||||
* First checks if the connection pool is still valid. If yes, we fetch a connection from the pool and return
|
* First checks if the connection pool is still valid. If yes, we fetch a connection from the pool and return
|
||||||
* In case a connection is not available in the pool, SQL Exception is thrown
|
* In case a connection is not available in the pool, SQL Exception is thrown
|
||||||
*
|
*
|
||||||
* @param hikariDSConnectionPool
|
* @param connectionPool
|
||||||
* @return SQL Connection
|
* @return SQL Connection
|
||||||
*/
|
*/
|
||||||
public static Connection getConnectionFromConnectionPool(HikariDataSource hikariDSConnectionPool) throws SQLException {
|
|
||||||
|
|
||||||
if (hikariDSConnectionPool == null || hikariDSConnectionPool.isClosed() || !hikariDSConnectionPool.isRunning()) {
|
|
||||||
log.debug("Encountered stale connection pool in SQL Server plugin. Reporting back.");
|
|
||||||
throw new StaleConnectionException();
|
|
||||||
}
|
|
||||||
|
|
||||||
Connection sqlDataSourceConnection = hikariDSConnectionPool.getConnection();
|
|
||||||
|
|
||||||
return sqlDataSourceConnection;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void logHikariCPStatus(String logPrefix, HikariDataSource connectionPool) {
|
public static void logHikariCPStatus(String logPrefix, HikariDataSource connectionPool) {
|
||||||
HikariPoolMXBean poolProxy = connectionPool.getHikariPoolMXBean();
|
HikariPoolMXBean poolProxy = connectionPool.getHikariPoolMXBean();
|
||||||
int idleConnections = poolProxy.getIdleConnections();
|
int idleConnections = poolProxy.getIdleConnections();
|
||||||
|
|
@ -305,4 +298,36 @@ public class MssqlDatasourceUtils {
|
||||||
return MessageFormat.format("{0}={1}", columnNameToSampleColumnDataMap.keySet().stream().findFirst().orElse(
|
return MessageFormat.format("{0}={1}", columnNameToSampleColumnDataMap.keySet().stream().findFirst().orElse(
|
||||||
"id"), columnNameToSampleColumnDataMap.values().stream().findFirst().orElse("'uid'"));
|
"id"), columnNameToSampleColumnDataMap.values().stream().findFirst().orElse("'uid'"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void checkHikariCPConnectionPoolValidity(HikariDataSource connectionPool, String pluginName) throws StaleConnectionException {
|
||||||
|
if (connectionPool == null || connectionPool.isClosed() || !connectionPool.isRunning()) {
|
||||||
|
String printMessage = MessageFormat.format(Thread.currentThread().getName() +
|
||||||
|
": Encountered stale connection pool in {0} plugin. Reporting back.", pluginName);
|
||||||
|
System.out.println(printMessage);
|
||||||
|
|
||||||
|
if (connectionPool == null) {
|
||||||
|
throw new StaleConnectionException(CONNECTION_POOL_NULL_ERROR_MSG);
|
||||||
|
}
|
||||||
|
else if (connectionPool.isClosed()) {
|
||||||
|
throw new StaleConnectionException(CONNECTION_POOL_CLOSED_ERROR_MSG);
|
||||||
|
}
|
||||||
|
else if (!connectionPool.isRunning()) {
|
||||||
|
throw new StaleConnectionException(CONNECTION_POOL_NOT_RUNNING_ERROR_MSG);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/**
|
||||||
|
* Ideally, code flow is never expected to reach here. However, this section has been added to catch
|
||||||
|
* those cases wherein a developer updates the parent if condition but does not update the nested
|
||||||
|
* if else conditions.
|
||||||
|
*/
|
||||||
|
throw new StaleConnectionException(UNKNOWN_CONNECTION_ERROR_MSG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Connection getConnectionFromHikariConnectionPool(HikariDataSource connectionPool,
|
||||||
|
String pluginName) throws SQLException {
|
||||||
|
checkHikariCPConnectionPoolValidity(connectionPool, pluginName);
|
||||||
|
return connectionPool.getConnection();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import com.appsmith.external.models.DBAuth;
|
||||||
import com.appsmith.external.models.DatasourceConfiguration;
|
import com.appsmith.external.models.DatasourceConfiguration;
|
||||||
import com.appsmith.external.models.Endpoint;
|
import com.appsmith.external.models.Endpoint;
|
||||||
import com.appsmith.external.models.SSLDetails;
|
import com.appsmith.external.models.SSLDetails;
|
||||||
|
import com.external.plugins.utils.MssqlDatasourceUtils;
|
||||||
import com.zaxxer.hikari.HikariDataSource;
|
import com.zaxxer.hikari.HikariDataSource;
|
||||||
import org.testcontainers.containers.MSSQLServerContainer;
|
import org.testcontainers.containers.MSSQLServerContainer;
|
||||||
import org.testcontainers.utility.DockerImageName;
|
import org.testcontainers.utility.DockerImageName;
|
||||||
|
|
@ -12,13 +13,15 @@ import java.sql.SQLException;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static com.external.plugins.utils.MssqlDatasourceUtils.getConnectionFromConnectionPool;
|
import static com.appsmith.external.constants.PluginConstants.PluginName.MSSQL_PLUGIN_NAME;
|
||||||
import static com.external.plugins.utils.MssqlExecuteUtils.closeConnectionPostExecution;
|
import static com.external.plugins.utils.MssqlExecuteUtils.closeConnectionPostExecution;
|
||||||
|
|
||||||
public class MssqlTestDBContainerManager {
|
public class MssqlTestDBContainerManager {
|
||||||
|
|
||||||
static MssqlPlugin.MssqlPluginExecutor mssqlPluginExecutor = new MssqlPlugin.MssqlPluginExecutor();
|
static MssqlPlugin.MssqlPluginExecutor mssqlPluginExecutor = new MssqlPlugin.MssqlPluginExecutor();
|
||||||
|
|
||||||
|
public static MssqlDatasourceUtils mssqlDatasourceUtils = new MssqlDatasourceUtils();
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
public static MSSQLServerContainer getMssqlDBForTest() {
|
public static MSSQLServerContainer getMssqlDBForTest() {
|
||||||
return new MSSQLServerContainer<>(
|
return new MSSQLServerContainer<>(
|
||||||
|
|
@ -56,7 +59,8 @@ public class MssqlTestDBContainerManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void runSQLQueryOnMssqlTestDB(String sqlQuery, HikariDataSource sharedConnectionPool) throws SQLException {
|
static void runSQLQueryOnMssqlTestDB(String sqlQuery, HikariDataSource sharedConnectionPool) throws SQLException {
|
||||||
java.sql.Connection connectionFromPool = getConnectionFromConnectionPool(sharedConnectionPool);
|
java.sql.Connection connectionFromPool =
|
||||||
|
mssqlDatasourceUtils.getConnectionFromHikariConnectionPool(sharedConnectionPool, MSSQL_PLUGIN_NAME);
|
||||||
Statement statement = connectionFromPool.createStatement();
|
Statement statement = connectionFromPool.createStatement();
|
||||||
statement.execute(sqlQuery);
|
statement.execute(sqlQuery);
|
||||||
closeConnectionPostExecution(null, statement, null, connectionFromPool);
|
closeConnectionPostExecution(null, statement, null, connectionFromPool);
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mariadb</groupId>
|
<groupId>org.mariadb</groupId>
|
||||||
<artifactId>r2dbc-mariadb</artifactId>
|
<artifactId>r2dbc-mariadb</artifactId>
|
||||||
<version>1.1.3</version>
|
<version>1.1.4</version>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<groupId>io.netty</groupId>
|
<groupId>io.netty</groupId>
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ import static com.appsmith.external.helpers.PluginUtils.MATCH_QUOTED_WORDS_REGEX
|
||||||
import static com.appsmith.external.helpers.PluginUtils.getIdenticalColumns;
|
import static com.appsmith.external.helpers.PluginUtils.getIdenticalColumns;
|
||||||
import static com.appsmith.external.helpers.PluginUtils.getPSParamLabel;
|
import static com.appsmith.external.helpers.PluginUtils.getPSParamLabel;
|
||||||
import static com.appsmith.external.helpers.SmartSubstitutionHelper.replaceQuestionMarkWithDollarIndex;
|
import static com.appsmith.external.helpers.SmartSubstitutionHelper.replaceQuestionMarkWithDollarIndex;
|
||||||
|
import static com.external.plugins.exceptions.MySQLErrorMessages.CONNECTION_VALIDITY_CHECK_FAILED_ERROR_MSG;
|
||||||
import static com.external.utils.MySqlDatasourceUtils.getNewConnectionPool;
|
import static com.external.utils.MySqlDatasourceUtils.getNewConnectionPool;
|
||||||
import static com.external.utils.MySqlGetStructureUtils.getKeyInfo;
|
import static com.external.utils.MySqlGetStructureUtils.getKeyInfo;
|
||||||
import static com.external.utils.MySqlGetStructureUtils.getTableInfo;
|
import static com.external.utils.MySqlGetStructureUtils.getTableInfo;
|
||||||
|
|
@ -256,9 +257,9 @@ public class MySqlPlugin extends BasePlugin {
|
||||||
connectionPool.create(),
|
connectionPool.create(),
|
||||||
connection -> {
|
connection -> {
|
||||||
// TODO: add JUnit TC for the `connection.validate` check. Not sure how to do it at the moment.
|
// TODO: add JUnit TC for the `connection.validate` check. Not sure how to do it at the moment.
|
||||||
Flux<Result> resultFlux = Mono.from(connection.validate(ValidationDepth.REMOTE))
|
Flux<Result> resultFlux = Mono.from(connection.validate(ValidationDepth.LOCAL))
|
||||||
.timeout(Duration.ofSeconds(VALIDATION_CHECK_TIMEOUT))
|
.timeout(Duration.ofSeconds(VALIDATION_CHECK_TIMEOUT))
|
||||||
.onErrorMap(TimeoutException.class, error -> new StaleConnectionException())
|
.onErrorMap(TimeoutException.class, error -> new StaleConnectionException(error.getMessage()))
|
||||||
.flatMapMany(isValid -> {
|
.flatMapMany(isValid -> {
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
return createAndExecuteQueryFromConnection(finalQuery,
|
return createAndExecuteQueryFromConnection(finalQuery,
|
||||||
|
|
@ -269,7 +270,7 @@ public class MySqlPlugin extends BasePlugin {
|
||||||
requestData,
|
requestData,
|
||||||
psParams);
|
psParams);
|
||||||
}
|
}
|
||||||
return Flux.error(new StaleConnectionException());
|
return Flux.error(new StaleConnectionException(CONNECTION_VALIDITY_CHECK_FAILED_ERROR_MSG));
|
||||||
});
|
});
|
||||||
|
|
||||||
Mono<List<Map<String, Object>>> resultMono;
|
Mono<List<Map<String, Object>>> resultMono;
|
||||||
|
|
@ -348,9 +349,10 @@ public class MySqlPlugin extends BasePlugin {
|
||||||
},
|
},
|
||||||
Connection::close
|
Connection::close
|
||||||
)
|
)
|
||||||
.onErrorMap(TimeoutException.class, error -> new StaleConnectionException())
|
.onErrorMap(TimeoutException.class, error -> new StaleConnectionException(error.getMessage()))
|
||||||
.onErrorMap(PoolShutdownException.class, error -> new StaleConnectionException())
|
.onErrorMap(PoolShutdownException.class, error -> new StaleConnectionException(error.getMessage()))
|
||||||
.onErrorMap(R2dbcNonTransientResourceException.class, error -> new StaleConnectionException())
|
.onErrorMap(R2dbcNonTransientResourceException.class, error -> new StaleConnectionException(error.getMessage()))
|
||||||
|
.onErrorMap(IllegalStateException.class, error -> new StaleConnectionException(error.getMessage()))
|
||||||
.subscribeOn(scheduler);
|
.subscribeOn(scheduler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -579,60 +581,58 @@ public class MySqlPlugin extends BasePlugin {
|
||||||
|
|
||||||
return Mono.usingWhen(
|
return Mono.usingWhen(
|
||||||
connectionPool.create(),
|
connectionPool.create(),
|
||||||
connection -> {
|
connection -> Mono.from(connection.validate(ValidationDepth.REMOTE))
|
||||||
return Mono.from(connection.validate(ValidationDepth.REMOTE))
|
.timeout(Duration.ofSeconds(VALIDATION_CHECK_TIMEOUT))
|
||||||
.timeout(Duration.ofSeconds(VALIDATION_CHECK_TIMEOUT))
|
.onErrorMap(TimeoutException.class, error -> new StaleConnectionException(error.getMessage()))
|
||||||
.onErrorMap(TimeoutException.class, error -> new StaleConnectionException())
|
.flatMapMany(isValid -> {
|
||||||
.flatMapMany(isValid -> {
|
if (isValid) {
|
||||||
if (isValid) {
|
return connection.createStatement(COLUMNS_QUERY).execute();
|
||||||
return connection.createStatement(COLUMNS_QUERY).execute();
|
} else {
|
||||||
} else {
|
return Flux.error(new StaleConnectionException(CONNECTION_VALIDITY_CHECK_FAILED_ERROR_MSG));
|
||||||
return Flux.error(new StaleConnectionException());
|
}
|
||||||
}
|
})
|
||||||
})
|
.flatMap(result -> {
|
||||||
.flatMap(result -> {
|
return result.map((row, meta) -> {
|
||||||
return result.map((row, meta) -> {
|
getTableInfo(row, meta, tablesByName);
|
||||||
getTableInfo(row, meta, tablesByName);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
});
|
|
||||||
})
|
|
||||||
.collectList()
|
|
||||||
.thenMany(Flux.from(connection.createStatement(KEYS_QUERY).execute()))
|
|
||||||
.flatMap(result -> {
|
|
||||||
return result.map((row, meta) -> {
|
|
||||||
getKeyInfo(row, meta, tablesByName, keyRegistry);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.collectList()
|
|
||||||
.map(list -> {
|
|
||||||
/* Get templates for each table and put those in. */
|
|
||||||
getTemplates(tablesByName);
|
|
||||||
structure.setTables(new ArrayList<>(tablesByName.values()));
|
|
||||||
for (DatasourceStructure.Table table : structure.getTables()) {
|
|
||||||
table.getKeys().sort(Comparator.naturalOrder());
|
|
||||||
}
|
|
||||||
|
|
||||||
return structure;
|
|
||||||
})
|
|
||||||
.onErrorMap(e -> {
|
|
||||||
if (!(e instanceof AppsmithPluginException) && !(e instanceof StaleConnectionException)) {
|
|
||||||
return new AppsmithPluginException(
|
|
||||||
AppsmithPluginError.PLUGIN_GET_STRUCTURE_ERROR,
|
|
||||||
MySQLErrorMessages.GET_STRUCTURE_ERROR_MSG,
|
|
||||||
e.getMessage()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return e;
|
|
||||||
});
|
});
|
||||||
},
|
})
|
||||||
|
.collectList()
|
||||||
|
.thenMany(Flux.from(connection.createStatement(KEYS_QUERY).execute()))
|
||||||
|
.flatMap(result -> {
|
||||||
|
return result.map((row, meta) -> {
|
||||||
|
getKeyInfo(row, meta, tablesByName, keyRegistry);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.collectList()
|
||||||
|
.map(list -> {
|
||||||
|
/* Get templates for each table and put those in. */
|
||||||
|
getTemplates(tablesByName);
|
||||||
|
structure.setTables(new ArrayList<>(tablesByName.values()));
|
||||||
|
for (DatasourceStructure.Table table : structure.getTables()) {
|
||||||
|
table.getKeys().sort(Comparator.naturalOrder());
|
||||||
|
}
|
||||||
|
|
||||||
|
return structure;
|
||||||
|
})
|
||||||
|
.onErrorMap(e -> {
|
||||||
|
if (!(e instanceof AppsmithPluginException) && !(e instanceof StaleConnectionException)) {
|
||||||
|
return new AppsmithPluginException(
|
||||||
|
AppsmithPluginError.PLUGIN_GET_STRUCTURE_ERROR,
|
||||||
|
MySQLErrorMessages.GET_STRUCTURE_ERROR_MSG,
|
||||||
|
e.getMessage()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}),
|
||||||
Connection::close
|
Connection::close
|
||||||
)
|
)
|
||||||
.onErrorMap(TimeoutException.class, error -> new StaleConnectionException())
|
.onErrorMap(TimeoutException.class, error -> new StaleConnectionException(error.getMessage()))
|
||||||
.onErrorMap(PoolShutdownException.class, error -> new StaleConnectionException())
|
.onErrorMap(PoolShutdownException.class, error -> new StaleConnectionException(error.getMessage()))
|
||||||
.subscribeOn(scheduler);
|
.subscribeOn(scheduler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
package com.external.plugins.exceptions;
|
package com.external.plugins.exceptions;
|
||||||
|
|
||||||
public class MySQLErrorMessages {
|
import com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages;
|
||||||
private MySQLErrorMessages() {
|
import lombok.AccessLevel;
|
||||||
//Prevents instantiation
|
import lombok.NoArgsConstructor;
|
||||||
}
|
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE) // To prevent instantiation
|
||||||
|
public class MySQLErrorMessages extends BasePluginErrorMessages {
|
||||||
public static final String MISSING_PARAMETER_QUERY_ERROR_MSG = "Missing required parameter: Query.";
|
public static final String MISSING_PARAMETER_QUERY_ERROR_MSG = "Missing required parameter: Query.";
|
||||||
|
|
||||||
public static final String IS_KEYWORD_NOT_SUPPORTED_IN_PS_ERROR_MSG = "Appsmith currently does not support the IS keyword with the prepared statement " +
|
public static final String IS_KEYWORD_NOT_SUPPORTED_IN_PS_ERROR_MSG = "Appsmith currently does not support the IS keyword with the prepared statement " +
|
||||||
|
|
@ -32,7 +34,6 @@ public class MySQLErrorMessages {
|
||||||
public static final String DS_MISSING_DATABASE_NAME_ERROR_MSG = "Missing database name.";
|
public static final String DS_MISSING_DATABASE_NAME_ERROR_MSG = "Missing database name.";
|
||||||
public static final String DS_SSL_CONFIGURATION_FETCHING_FAILED_ERROR_MSG = "Appsmith server has failed to fetch SSL configuration from datasource configuration form. " +
|
public static final String DS_SSL_CONFIGURATION_FETCHING_FAILED_ERROR_MSG = "Appsmith server has failed to fetch SSL configuration from datasource configuration form. " +
|
||||||
"Please reach out to Appsmith customer support to resolve this.";
|
"Please reach out to Appsmith customer support to resolve this.";
|
||||||
|
public static final String CONNECTION_VALIDITY_CHECK_FAILED_ERROR_MSG = "Connection obtained from connection pool" +
|
||||||
|
" is invalid.";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import com.appsmith.external.models.Endpoint;
|
||||||
import com.appsmith.external.models.Property;
|
import com.appsmith.external.models.Property;
|
||||||
import com.appsmith.external.models.SSLDetails;
|
import com.appsmith.external.models.SSLDetails;
|
||||||
import com.external.plugins.exceptions.MySQLErrorMessages;
|
import com.external.plugins.exceptions.MySQLErrorMessages;
|
||||||
import com.external.plugins.exceptions.MySQLPluginError;
|
|
||||||
import io.r2dbc.pool.ConnectionPool;
|
import io.r2dbc.pool.ConnectionPool;
|
||||||
import io.r2dbc.pool.ConnectionPoolConfiguration;
|
import io.r2dbc.pool.ConnectionPoolConfiguration;
|
||||||
import io.r2dbc.spi.ConnectionFactoryOptions;
|
import io.r2dbc.spi.ConnectionFactoryOptions;
|
||||||
|
|
@ -25,14 +24,40 @@ import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static io.r2dbc.pool.PoolingConnectionFactoryProvider.MAX_SIZE;
|
|
||||||
import static io.r2dbc.spi.ConnectionFactoryOptions.SSL;
|
import static io.r2dbc.spi.ConnectionFactoryOptions.SSL;
|
||||||
|
|
||||||
public class MySqlDatasourceUtils {
|
public class MySqlDatasourceUtils {
|
||||||
|
|
||||||
public static int MAX_CONNECTION_POOL_SIZE = 5;
|
public static int MAX_CONNECTION_POOL_SIZE = 5;
|
||||||
|
|
||||||
private static final Duration MAX_IDLE_TIME = Duration.ofMinutes(10);
|
/**
|
||||||
|
* 1 sec is the recommended value as shown in the example here:
|
||||||
|
* https://mariadb.com/docs/xpand/connect/programming-languages/java-r2dbc/native/connection-pools/
|
||||||
|
*
|
||||||
|
* Current understanding is that the issue mentioned in #17324 is because of at least one of the connections
|
||||||
|
* malfunctioning and causing the reactor thread pool / scheduler to get stuck and not schedule new tasks.
|
||||||
|
* Setting max idle time value to 1 sec could also be seen as a precaution move to make sure that we don't land
|
||||||
|
* into a situation where an idle thread can malfunction.
|
||||||
|
*/
|
||||||
|
private static final Duration MAX_IDLE_TIME = Duration.ofSeconds(1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current understanding is that the issue mentioned in #17324 is because of at least one of the connections
|
||||||
|
* malfunctioning and causing the reactor thread pool / scheduler to get stuck and not schedule new tasks.
|
||||||
|
* Setting max lifetime value to 5 min is a precaution move to make sure that we don't land into a situation
|
||||||
|
* where an older connection can malfunction.
|
||||||
|
* To understand what this config means please check here: https://github.com/r2dbc/r2dbc-pool
|
||||||
|
*/
|
||||||
|
private static final Duration MAX_LIFE_TIME = Duration.ofMinutes(5);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current understanding is that the issue mentioned in #17324 is because of at least one of the connections
|
||||||
|
* malfunctioning and causing the reactor thread pool / scheduler to get stuck and not schedule new tasks.
|
||||||
|
* Setting eviction time value to 5 min is a precaution move to make sure that we don't land into a situation
|
||||||
|
* where an older connection can malfunction.
|
||||||
|
* To understand what this config means please check here: https://github.com/r2dbc/r2dbc-pool
|
||||||
|
*/
|
||||||
|
public static final Duration BACKGROUND_EVICTION_TIME = Duration.ofMinutes(5);
|
||||||
|
|
||||||
public static ConnectionFactoryOptions.Builder getBuilder(DatasourceConfiguration datasourceConfiguration) {
|
public static ConnectionFactoryOptions.Builder getBuilder(DatasourceConfiguration datasourceConfiguration) {
|
||||||
DBAuth authentication = (DBAuth) datasourceConfiguration.getAuthentication();
|
DBAuth authentication = (DBAuth) datasourceConfiguration.getAuthentication();
|
||||||
|
|
@ -188,7 +213,10 @@ public class MySqlDatasourceUtils {
|
||||||
ConnectionPoolConfiguration configuration = ConnectionPoolConfiguration.builder(connectionFactory)
|
ConnectionPoolConfiguration configuration = ConnectionPoolConfiguration.builder(connectionFactory)
|
||||||
.maxIdleTime(MAX_IDLE_TIME)
|
.maxIdleTime(MAX_IDLE_TIME)
|
||||||
.maxSize(MAX_CONNECTION_POOL_SIZE)
|
.maxSize(MAX_CONNECTION_POOL_SIZE)
|
||||||
|
.backgroundEvictionInterval(BACKGROUND_EVICTION_TIME)
|
||||||
|
.maxLifeTime(MAX_LIFE_TIME)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
return new ConnectionPool(configuration);
|
return new ConnectionPool(configuration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,117 @@
|
||||||
|
package com.external.plugins;
|
||||||
|
|
||||||
|
import com.appsmith.external.dtos.ExecuteActionDTO;
|
||||||
|
import com.appsmith.external.exceptions.pluginExceptions.StaleConnectionException;
|
||||||
|
import com.appsmith.external.models.ActionConfiguration;
|
||||||
|
import com.appsmith.external.models.ActionExecutionResult;
|
||||||
|
import com.external.utils.MySqlDatasourceUtils;
|
||||||
|
import io.r2dbc.pool.ConnectionPool;
|
||||||
|
import io.r2dbc.spi.Connection;
|
||||||
|
import io.r2dbc.spi.R2dbcNonTransientResourceException;
|
||||||
|
import io.r2dbc.spi.ValidationDepth;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
import reactor.pool.PoolShutdownException;
|
||||||
|
import reactor.test.StepVerifier;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
import static com.external.plugins.exceptions.MySQLErrorMessages.CONNECTION_VALIDITY_CHECK_FAILED_ERROR_MSG;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
public class MySqlStaleConnectionErrorMessageTest {
|
||||||
|
static MySqlPlugin.MySqlPluginExecutor pluginExecutor = new MySqlPlugin.MySqlPluginExecutor();
|
||||||
|
static MySqlDatasourceUtils mysqlDatasourceUtils = new MySqlDatasourceUtils();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStaleConnectionExceptionReturnsUpstreamErrorOnTimeoutError() throws TimeoutException {
|
||||||
|
ActionConfiguration actionConfiguration = new ActionConfiguration();
|
||||||
|
actionConfiguration.setBody("select 1;");
|
||||||
|
ConnectionPool mockConnectionPool = mock(ConnectionPool.class);
|
||||||
|
String expectedErrorMessage = "Timeout exception from MockConnectionPool";
|
||||||
|
when(mockConnectionPool.create()).thenReturn(Mono.error(new TimeoutException(expectedErrorMessage)));
|
||||||
|
Mono<ActionExecutionResult> actionExecutionResultMono = pluginExecutor.executeCommon(mockConnectionPool, actionConfiguration, false, List.of(),
|
||||||
|
new ExecuteActionDTO(), new HashMap<>());
|
||||||
|
StepVerifier.create(actionExecutionResultMono)
|
||||||
|
.expectErrorSatisfies(error -> {
|
||||||
|
assertTrue(error instanceof StaleConnectionException);
|
||||||
|
assertEquals(expectedErrorMessage, error.getMessage());
|
||||||
|
})
|
||||||
|
.verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStaleConnectionExceptionReturnsUpstreamErrorOnPoolShutdownError() throws TimeoutException {
|
||||||
|
ActionConfiguration actionConfiguration = new ActionConfiguration();
|
||||||
|
actionConfiguration.setBody("select 1;");
|
||||||
|
ConnectionPool mockConnectionPool = mock(ConnectionPool.class);
|
||||||
|
String expectedErrorMessage = "Timeout exception from MockConnectionPool";
|
||||||
|
when(mockConnectionPool.create()).thenReturn(Mono.error(new PoolShutdownException(expectedErrorMessage)));
|
||||||
|
Mono<ActionExecutionResult> actionExecutionResultMono = pluginExecutor.executeCommon(mockConnectionPool, actionConfiguration, false, List.of(),
|
||||||
|
new ExecuteActionDTO(), new HashMap<>());
|
||||||
|
StepVerifier.create(actionExecutionResultMono)
|
||||||
|
.expectErrorSatisfies(error -> {
|
||||||
|
assertTrue(error instanceof StaleConnectionException);
|
||||||
|
assertEquals(expectedErrorMessage, error.getMessage());
|
||||||
|
})
|
||||||
|
.verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStaleConnectionExceptionReturnsUpstreamErrorOnIllegalStateError() throws TimeoutException {
|
||||||
|
ActionConfiguration actionConfiguration = new ActionConfiguration();
|
||||||
|
actionConfiguration.setBody("select 1;");
|
||||||
|
ConnectionPool mockConnectionPool = mock(ConnectionPool.class);
|
||||||
|
String expectedErrorMessage = "Timeout exception from MockConnectionPool";
|
||||||
|
when(mockConnectionPool.create()).thenReturn(Mono.error(new IllegalStateException(expectedErrorMessage)));
|
||||||
|
Mono<ActionExecutionResult> actionExecutionResultMono = pluginExecutor.executeCommon(mockConnectionPool, actionConfiguration, false, List.of(),
|
||||||
|
new ExecuteActionDTO(), new HashMap<>());
|
||||||
|
StepVerifier.create(actionExecutionResultMono)
|
||||||
|
.expectErrorSatisfies(error -> {
|
||||||
|
assertTrue(error instanceof StaleConnectionException);
|
||||||
|
assertEquals(expectedErrorMessage, error.getMessage());
|
||||||
|
})
|
||||||
|
.verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStaleConnectionExceptionReturnsUpstreamErrorOnResourceError() throws TimeoutException {
|
||||||
|
ActionConfiguration actionConfiguration = new ActionConfiguration();
|
||||||
|
actionConfiguration.setBody("select 1;");
|
||||||
|
ConnectionPool mockConnectionPool = mock(ConnectionPool.class);
|
||||||
|
String expectedErrorMessage = "Timeout exception from MockConnectionPool";
|
||||||
|
when(mockConnectionPool.create()).thenReturn(Mono.error(new R2dbcNonTransientResourceException(expectedErrorMessage)));
|
||||||
|
Mono<ActionExecutionResult> actionExecutionResultMono = pluginExecutor.executeCommon(mockConnectionPool, actionConfiguration, false, List.of(),
|
||||||
|
new ExecuteActionDTO(), new HashMap<>());
|
||||||
|
StepVerifier.create(actionExecutionResultMono)
|
||||||
|
.expectErrorSatisfies(error -> {
|
||||||
|
assertTrue(error instanceof StaleConnectionException);
|
||||||
|
assertEquals(expectedErrorMessage, error.getMessage());
|
||||||
|
})
|
||||||
|
.verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStaleConnectionExceptionReturnsUpstreamErrorOnInvalidConnection() throws TimeoutException {
|
||||||
|
ActionConfiguration actionConfiguration = new ActionConfiguration();
|
||||||
|
actionConfiguration.setBody("select 1;");
|
||||||
|
ConnectionPool mockConnectionPool = mock(ConnectionPool.class);
|
||||||
|
Connection mockConnection = mock(Connection.class);
|
||||||
|
when(mockConnectionPool.create()).thenReturn(Mono.just(mockConnection));
|
||||||
|
when(mockConnection.validate(ValidationDepth.LOCAL)).thenReturn(Mono.just(false));
|
||||||
|
when(mockConnection.close()).thenReturn(Mono.empty());
|
||||||
|
Mono<ActionExecutionResult> actionExecutionResultMono = pluginExecutor.executeCommon(mockConnectionPool, actionConfiguration, false, List.of(),
|
||||||
|
new ExecuteActionDTO(), new HashMap<>());
|
||||||
|
StepVerifier.create(actionExecutionResultMono)
|
||||||
|
.expectErrorSatisfies(error -> {
|
||||||
|
assertTrue(error instanceof StaleConnectionException);
|
||||||
|
assertEquals(CONNECTION_VALIDITY_CHECK_FAILED_ERROR_MSG, error.getMessage());
|
||||||
|
})
|
||||||
|
.verify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -58,6 +58,7 @@ import java.util.stream.IntStream;
|
||||||
import static com.appsmith.external.constants.ActionConstants.ACTION_CONFIGURATION_BODY;
|
import static com.appsmith.external.constants.ActionConstants.ACTION_CONFIGURATION_BODY;
|
||||||
import static com.appsmith.external.constants.CommonFieldName.BODY;
|
import static com.appsmith.external.constants.CommonFieldName.BODY;
|
||||||
import static com.appsmith.external.constants.CommonFieldName.PREPARED_STATEMENT;
|
import static com.appsmith.external.constants.CommonFieldName.PREPARED_STATEMENT;
|
||||||
|
import static com.appsmith.external.constants.PluginConstants.PluginName.ORACLE_PLUGIN_NAME;
|
||||||
import static com.appsmith.external.helpers.PluginUtils.OBJECT_TYPE;
|
import static com.appsmith.external.helpers.PluginUtils.OBJECT_TYPE;
|
||||||
import static com.appsmith.external.helpers.PluginUtils.STRING_TYPE;
|
import static com.appsmith.external.helpers.PluginUtils.STRING_TYPE;
|
||||||
import static com.appsmith.external.helpers.PluginUtils.getDataValueSafelyFromFormData;
|
import static com.appsmith.external.helpers.PluginUtils.getDataValueSafelyFromFormData;
|
||||||
|
|
@ -67,7 +68,6 @@ import static com.appsmith.external.helpers.PluginUtils.setDataValueSafelyInForm
|
||||||
import static com.appsmith.external.helpers.SmartSubstitutionHelper.replaceQuestionMarkWithDollarIndex;
|
import static com.appsmith.external.helpers.SmartSubstitutionHelper.replaceQuestionMarkWithDollarIndex;
|
||||||
import static com.external.plugins.utils.OracleDatasourceUtils.JDBC_DRIVER;
|
import static com.external.plugins.utils.OracleDatasourceUtils.JDBC_DRIVER;
|
||||||
import static com.external.plugins.utils.OracleDatasourceUtils.createConnectionPool;
|
import static com.external.plugins.utils.OracleDatasourceUtils.createConnectionPool;
|
||||||
import static com.external.plugins.utils.OracleDatasourceUtils.getConnectionFromConnectionPool;
|
|
||||||
import static com.external.plugins.utils.OracleDatasourceUtils.logHikariCPStatus;
|
import static com.external.plugins.utils.OracleDatasourceUtils.logHikariCPStatus;
|
||||||
import static com.external.plugins.utils.OracleExecuteUtils.closeConnectionPostExecution;
|
import static com.external.plugins.utils.OracleExecuteUtils.closeConnectionPostExecution;
|
||||||
import static com.external.plugins.utils.OracleExecuteUtils.isPLSQL;
|
import static com.external.plugins.utils.OracleExecuteUtils.isPLSQL;
|
||||||
|
|
@ -79,6 +79,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class OraclePlugin extends BasePlugin {
|
public class OraclePlugin extends BasePlugin {
|
||||||
|
public static final OracleDatasourceUtils oracleDatasourceUtils = new OracleDatasourceUtils();
|
||||||
|
|
||||||
public OraclePlugin(PluginWrapper wrapper) {
|
public OraclePlugin(PluginWrapper wrapper) {
|
||||||
super(wrapper);
|
super(wrapper);
|
||||||
|
|
@ -197,7 +198,9 @@ public class OraclePlugin extends BasePlugin {
|
||||||
Connection connectionFromPool;
|
Connection connectionFromPool;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
connectionFromPool = getConnectionFromConnectionPool(connectionPool);
|
connectionFromPool =
|
||||||
|
oracleDatasourceUtils.getConnectionFromHikariConnectionPool(connectionPool,
|
||||||
|
ORACLE_PLUGIN_NAME);
|
||||||
} catch (SQLException | StaleConnectionException e) {
|
} catch (SQLException | StaleConnectionException e) {
|
||||||
// The function can throw either StaleConnectionException or SQLException. The underlying hikari
|
// The function can throw either StaleConnectionException or SQLException. The underlying hikari
|
||||||
// library throws SQLException in case the pool is closed or there is an issue initializing
|
// library throws SQLException in case the pool is closed or there is an issue initializing
|
||||||
|
|
@ -205,7 +208,8 @@ public class OraclePlugin extends BasePlugin {
|
||||||
// and should then trigger the destruction and recreation of the pool.
|
// and should then trigger the destruction and recreation of the pool.
|
||||||
log.debug("Exception Occurred while getting connection from pool" + e.getMessage());
|
log.debug("Exception Occurred while getting connection from pool" + e.getMessage());
|
||||||
e.printStackTrace(System.out);
|
e.printStackTrace(System.out);
|
||||||
return Mono.error(e instanceof StaleConnectionException ? e : new StaleConnectionException());
|
return Mono.error(e instanceof StaleConnectionException ? e :
|
||||||
|
new StaleConnectionException(e.getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Map<String, Object>> rowsList = new ArrayList<>(50);
|
List<Map<String, Object>> rowsList = new ArrayList<>(50);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
package com.external.plugins.exceptions;
|
package com.external.plugins.exceptions;
|
||||||
|
|
||||||
public class OracleErrorMessages {
|
import com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages;
|
||||||
private OracleErrorMessages() {
|
import lombok.AccessLevel;
|
||||||
//Prevents instantiation
|
import lombok.NoArgsConstructor;
|
||||||
}
|
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE) // To prevent instantiation
|
||||||
|
public class OracleErrorMessages extends BasePluginErrorMessages {
|
||||||
public static final String MISSING_QUERY_ERROR_MSG = "Missing required parameter: Query.";
|
public static final String MISSING_QUERY_ERROR_MSG = "Missing required parameter: Query.";
|
||||||
|
|
||||||
public static final String QUERY_EXECUTION_FAILED_ERROR_MSG = "Your Oracle query failed to execute.";
|
public static final String QUERY_EXECUTION_FAILED_ERROR_MSG = "Your Oracle query failed to execute.";
|
||||||
|
|
@ -34,8 +36,6 @@ public class OracleErrorMessages {
|
||||||
|
|
||||||
public static final String DS_INVALID_HOSTNAME_ERROR_MSG = "Host value cannot contain `/` or `:` characters. Found `%s`.";
|
public static final String DS_INVALID_HOSTNAME_ERROR_MSG = "Host value cannot contain `/` or `:` characters. Found `%s`.";
|
||||||
|
|
||||||
public static final String DS_MISSING_CONNECTION_MODE_ERROR_MSG = "Missing connection mode.";
|
|
||||||
|
|
||||||
public static final String DS_MISSING_AUTHENTICATION_DETAILS_ERROR_MSG = "Missing authentication details.";
|
public static final String DS_MISSING_AUTHENTICATION_DETAILS_ERROR_MSG = "Missing authentication details.";
|
||||||
|
|
||||||
public static final String DS_MISSING_USERNAME_ERROR_MSG = "Missing username for authentication.";
|
public static final String DS_MISSING_USERNAME_ERROR_MSG = "Missing username for authentication.";
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,14 @@ import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static com.appsmith.external.constants.PluginConstants.PluginName.ORACLE_PLUGIN_NAME;
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.CONNECTION_POOL_CLOSED_ERROR_MSG;
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.CONNECTION_POOL_NOT_RUNNING_ERROR_MSG;
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.CONNECTION_POOL_NULL_ERROR_MSG;
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.UNKNOWN_CONNECTION_ERROR_MSG;
|
||||||
import static com.appsmith.external.helpers.PluginUtils.safelyCloseSingleConnectionFromHikariCP;
|
import static com.appsmith.external.helpers.PluginUtils.safelyCloseSingleConnectionFromHikariCP;
|
||||||
import static com.external.plugins.OraclePlugin.OraclePluginExecutor.scheduler;
|
import static com.external.plugins.OraclePlugin.OraclePluginExecutor.scheduler;
|
||||||
|
import static com.external.plugins.OraclePlugin.oracleDatasourceUtils;
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
import static org.springframework.util.CollectionUtils.isEmpty;
|
import static org.springframework.util.CollectionUtils.isEmpty;
|
||||||
|
|
||||||
|
|
@ -155,13 +161,15 @@ public class OracleDatasourceUtils {
|
||||||
return Mono.fromSupplier(() -> {
|
return Mono.fromSupplier(() -> {
|
||||||
Connection connectionFromPool;
|
Connection connectionFromPool;
|
||||||
try {
|
try {
|
||||||
connectionFromPool = getConnectionFromConnectionPool(connectionPool);
|
connectionFromPool =
|
||||||
|
oracleDatasourceUtils.getConnectionFromHikariConnectionPool(connectionPool, ORACLE_PLUGIN_NAME);
|
||||||
} catch (SQLException | StaleConnectionException e) {
|
} catch (SQLException | StaleConnectionException e) {
|
||||||
// The function can throw either StaleConnectionException or SQLException. The
|
// The function can throw either StaleConnectionException or SQLException. The
|
||||||
// underlying hikari library throws SQLException in case the pool is closed or there is an issue
|
// underlying hikari library throws SQLException in case the pool is closed or there is an issue
|
||||||
// initializing the connection pool which can also be translated in our world to
|
// initializing the connection pool which can also be translated in our world to
|
||||||
// StaleConnectionException and should then trigger the destruction and recreation of the pool.
|
// StaleConnectionException and should then trigger the destruction and recreation of the pool.
|
||||||
return Mono.error(e instanceof StaleConnectionException ? e : new StaleConnectionException());
|
return Mono.error(e instanceof StaleConnectionException ? e :
|
||||||
|
new StaleConnectionException(e.getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
logHikariCPStatus("Before getting Oracle DB schema", connectionPool);
|
logHikariCPStatus("Before getting Oracle DB schema", connectionPool);
|
||||||
|
|
@ -439,21 +447,6 @@ public class OracleDatasourceUtils {
|
||||||
return datasource;
|
return datasource;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* First checks if the connection pool is still valid. If yes, we fetch a connection from the pool and return
|
|
||||||
* In case a connection is not available in the pool, SQL Exception is thrown
|
|
||||||
*/
|
|
||||||
public static java.sql.Connection getConnectionFromConnectionPool(HikariDataSource connectionPool) throws SQLException {
|
|
||||||
|
|
||||||
if (connectionPool == null || connectionPool.isClosed() || !connectionPool.isRunning()) {
|
|
||||||
System.out.println(Thread.currentThread().getName() +
|
|
||||||
": Encountered stale connection pool in Oracle plugin. Reporting back.");
|
|
||||||
throw new StaleConnectionException();
|
|
||||||
}
|
|
||||||
|
|
||||||
return connectionPool.getConnection();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void logHikariCPStatus(String logPrefix, HikariDataSource connectionPool) {
|
public static void logHikariCPStatus(String logPrefix, HikariDataSource connectionPool) {
|
||||||
HikariPoolMXBean poolProxy = connectionPool.getHikariPoolMXBean();
|
HikariPoolMXBean poolProxy = connectionPool.getHikariPoolMXBean();
|
||||||
int idleConnections = poolProxy.getIdleConnections();
|
int idleConnections = poolProxy.getIdleConnections();
|
||||||
|
|
@ -463,4 +456,36 @@ public class OracleDatasourceUtils {
|
||||||
log.debug(MessageFormat.format("{0}: Hikari Pool stats : active - {1} , idle - {2}, awaiting - {3} , total - {4}",
|
log.debug(MessageFormat.format("{0}: Hikari Pool stats : active - {1} , idle - {2}, awaiting - {3} , total - {4}",
|
||||||
logPrefix, activeConnections, idleConnections, threadsAwaitingConnection, totalConnections));
|
logPrefix, activeConnections, idleConnections, threadsAwaitingConnection, totalConnections));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void checkHikariCPConnectionPoolValidity(HikariDataSource connectionPool, String pluginName) throws StaleConnectionException {
|
||||||
|
if (connectionPool == null || connectionPool.isClosed() || !connectionPool.isRunning()) {
|
||||||
|
String printMessage = MessageFormat.format(Thread.currentThread().getName() +
|
||||||
|
": Encountered stale connection pool in {0} plugin. Reporting back.", pluginName);
|
||||||
|
System.out.println(printMessage);
|
||||||
|
|
||||||
|
if (connectionPool == null) {
|
||||||
|
throw new StaleConnectionException(CONNECTION_POOL_NULL_ERROR_MSG);
|
||||||
|
}
|
||||||
|
else if (connectionPool.isClosed()) {
|
||||||
|
throw new StaleConnectionException(CONNECTION_POOL_CLOSED_ERROR_MSG);
|
||||||
|
}
|
||||||
|
else if (!connectionPool.isRunning()) {
|
||||||
|
throw new StaleConnectionException(CONNECTION_POOL_NOT_RUNNING_ERROR_MSG);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/**
|
||||||
|
* Ideally, code flow is never expected to reach here. However, this section has been added to catch
|
||||||
|
* those cases wherein a developer updates the parent if condition but does not update the nested
|
||||||
|
* if else conditions.
|
||||||
|
*/
|
||||||
|
throw new StaleConnectionException(UNKNOWN_CONNECTION_ERROR_MSG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Connection getConnectionFromHikariConnectionPool(HikariDataSource connectionPool,
|
||||||
|
String pluginName) throws SQLException {
|
||||||
|
checkHikariCPConnectionPoolValidity(connectionPool, pluginName);
|
||||||
|
return connectionPool.getConnection();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,23 @@
|
||||||
package com.external.plugins;
|
package com.external.plugins;
|
||||||
|
|
||||||
|
import com.appsmith.external.exceptions.pluginExceptions.StaleConnectionException;
|
||||||
import com.external.plugins.exceptions.OraclePluginError;
|
import com.external.plugins.exceptions.OraclePluginError;
|
||||||
|
import com.zaxxer.hikari.HikariDataSource;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.CONNECTION_POOL_CLOSED_ERROR_MSG;
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.CONNECTION_POOL_NOT_RUNNING_ERROR_MSG;
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.CONNECTION_POOL_NULL_ERROR_MSG;
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.UNKNOWN_CONNECTION_ERROR_MSG;
|
||||||
|
import static com.external.plugins.OracleTestDBContainerManager.oracleDatasourceUtils;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class OraclePluginErrorsTest {
|
public class OraclePluginErrorsTest {
|
||||||
@Test
|
@Test
|
||||||
public void verifyUniquenessOfOraclePluginErrorCode() {
|
public void verifyUniquenessOfOraclePluginErrorCode() {
|
||||||
|
|
@ -15,4 +27,63 @@ public class OraclePluginErrorsTest {
|
||||||
.filter(appErrorCode -> appErrorCode.length() != 11 || !appErrorCode.startsWith("PE-ORC"))
|
.filter(appErrorCode -> appErrorCode.length() != 11 || !appErrorCode.startsWith("PE-ORC"))
|
||||||
.collect(Collectors.toList()).size() == 0);
|
.collect(Collectors.toList()).size() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not repeating this test for other plugins because (1) their implementation is identical to this one. (2) We
|
||||||
|
* want to re-factor this identical code into a common method in future (#24763) - could not be done right now because
|
||||||
|
* of some package dependency issues.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testStaleConnectionErrorHasUpstreamErrorWhenConnectionPoolIsNull() {
|
||||||
|
Exception exception = assertThrows(StaleConnectionException.class,
|
||||||
|
() -> oracleDatasourceUtils.checkHikariCPConnectionPoolValidity(null
|
||||||
|
, "pluginName"));
|
||||||
|
String expectedErrorMessage = CONNECTION_POOL_NULL_ERROR_MSG;
|
||||||
|
assertEquals(expectedErrorMessage, exception.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not repeating this test for other plugins because (1) their implementation is identical to this one. (2) We
|
||||||
|
* want to re-factor this identical code into a common method in future (#24763) - could not be done right now because
|
||||||
|
* of some package dependency issues.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testStaleConnectionErrorHasUpstreamErrorWhenConnectionPoolIsClosed() {
|
||||||
|
HikariDataSource mockConnectionPool = mock(HikariDataSource.class);
|
||||||
|
when(mockConnectionPool.isClosed()).thenReturn(true).thenReturn(true);
|
||||||
|
Exception exception = assertThrows(StaleConnectionException.class,
|
||||||
|
() -> oracleDatasourceUtils.checkHikariCPConnectionPoolValidity(mockConnectionPool, "pluginName"));
|
||||||
|
String expectedErrorMessage = CONNECTION_POOL_CLOSED_ERROR_MSG;
|
||||||
|
assertEquals(expectedErrorMessage, exception.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not repeating this test for other plugins because (1) their implementation is identical to this one. (2) We
|
||||||
|
* want to re-factor this identical code into a common method in future (#24763) - could not be done right now because
|
||||||
|
* of some package dependency issues.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testStaleConnectionErrorHasUpstreamErrorWhenConnectionPoolIsRunning() {
|
||||||
|
HikariDataSource mockConnectionPool = mock(HikariDataSource.class);
|
||||||
|
when(mockConnectionPool.isRunning()).thenReturn(false).thenReturn(false);
|
||||||
|
Exception exception = assertThrows(StaleConnectionException.class,
|
||||||
|
() -> oracleDatasourceUtils.checkHikariCPConnectionPoolValidity(mockConnectionPool, "pluginName"));
|
||||||
|
String expectedErrorMessage = CONNECTION_POOL_NOT_RUNNING_ERROR_MSG;
|
||||||
|
assertEquals(expectedErrorMessage, exception.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not repeating this test for other plugins because (1) their implementation is identical to this one. (2) We
|
||||||
|
* want to re-factor this identical code into a common method in future (#24763) - could not be done right now because
|
||||||
|
* of some package dependency issues.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testStaleConnectionErrorHasDefaultUpstreamError() {
|
||||||
|
HikariDataSource mockConnectionPool = mock(HikariDataSource.class);
|
||||||
|
when(mockConnectionPool.isRunning()).thenReturn(false).thenReturn(true);
|
||||||
|
Exception exception = assertThrows(StaleConnectionException.class,
|
||||||
|
() -> oracleDatasourceUtils.checkHikariCPConnectionPoolValidity(mockConnectionPool, "pluginName"));
|
||||||
|
String expectedErrorMessage = UNKNOWN_CONNECTION_ERROR_MSG;
|
||||||
|
assertEquals(expectedErrorMessage, exception.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import com.appsmith.external.models.DBAuth;
|
||||||
import com.appsmith.external.models.DatasourceConfiguration;
|
import com.appsmith.external.models.DatasourceConfiguration;
|
||||||
import com.appsmith.external.models.Endpoint;
|
import com.appsmith.external.models.Endpoint;
|
||||||
import com.appsmith.external.models.SSLDetails;
|
import com.appsmith.external.models.SSLDetails;
|
||||||
|
import com.external.plugins.utils.OracleDatasourceUtils;
|
||||||
import com.zaxxer.hikari.HikariDataSource;
|
import com.zaxxer.hikari.HikariDataSource;
|
||||||
import org.testcontainers.containers.OracleContainer;
|
import org.testcontainers.containers.OracleContainer;
|
||||||
|
|
||||||
|
|
@ -12,7 +13,7 @@ import java.sql.SQLException;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import static com.external.plugins.utils.OracleDatasourceUtils.getConnectionFromConnectionPool;
|
import static com.appsmith.external.constants.PluginConstants.PluginName.ORACLE_PLUGIN_NAME;
|
||||||
import static com.external.plugins.utils.OracleExecuteUtils.closeConnectionPostExecution;
|
import static com.external.plugins.utils.OracleExecuteUtils.closeConnectionPostExecution;
|
||||||
|
|
||||||
public class OracleTestDBContainerManager {
|
public class OracleTestDBContainerManager {
|
||||||
|
|
@ -20,6 +21,8 @@ public class OracleTestDBContainerManager {
|
||||||
public static final String ORACLE_PASSWORD = "testPassword";
|
public static final String ORACLE_PASSWORD = "testPassword";
|
||||||
public static final String ORACLE_DB_NAME = "testDB";
|
public static final String ORACLE_DB_NAME = "testDB";
|
||||||
public static final String ORACLE_DOCKER_HUB_CONTAINER = "gvenzl/oracle-xe:21-slim-faststart";
|
public static final String ORACLE_DOCKER_HUB_CONTAINER = "gvenzl/oracle-xe:21-slim-faststart";
|
||||||
|
|
||||||
|
public static OracleDatasourceUtils oracleDatasourceUtils = new OracleDatasourceUtils();
|
||||||
static OraclePlugin.OraclePluginExecutor oraclePluginExecutor = new OraclePlugin.OraclePluginExecutor();
|
static OraclePlugin.OraclePluginExecutor oraclePluginExecutor = new OraclePlugin.OraclePluginExecutor();
|
||||||
|
|
||||||
public static OracleContainer getOracleDBForTest() {
|
public static OracleContainer getOracleDBForTest() {
|
||||||
|
|
@ -49,7 +52,9 @@ public class OracleTestDBContainerManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void runSQLQueryOnOracleTestDB(String sqlQuery, HikariDataSource sharedConnectionPool) throws SQLException {
|
static void runSQLQueryOnOracleTestDB(String sqlQuery, HikariDataSource sharedConnectionPool) throws SQLException {
|
||||||
java.sql.Connection connectionFromPool = getConnectionFromConnectionPool(sharedConnectionPool);
|
java.sql.Connection connectionFromPool =
|
||||||
|
oracleDatasourceUtils.getConnectionFromHikariConnectionPool(sharedConnectionPool,
|
||||||
|
ORACLE_PLUGIN_NAME);
|
||||||
Statement statement = connectionFromPool.createStatement();
|
Statement statement = connectionFromPool.createStatement();
|
||||||
statement.execute(sqlQuery);
|
statement.execute(sqlQuery);
|
||||||
closeConnectionPostExecution(null, statement, null, connectionFromPool);
|
closeConnectionPostExecution(null, statement, null, connectionFromPool);
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import com.appsmith.external.services.SharedConfig;
|
||||||
import com.external.plugins.datatypes.PostgresSpecificDataTypes;
|
import com.external.plugins.datatypes.PostgresSpecificDataTypes;
|
||||||
import com.external.plugins.exceptions.PostgresErrorMessages;
|
import com.external.plugins.exceptions.PostgresErrorMessages;
|
||||||
import com.external.plugins.exceptions.PostgresPluginError;
|
import com.external.plugins.exceptions.PostgresPluginError;
|
||||||
|
import com.external.plugins.utils.PostgresDatasourceUtils;
|
||||||
import com.zaxxer.hikari.HikariConfig;
|
import com.zaxxer.hikari.HikariConfig;
|
||||||
import com.zaxxer.hikari.HikariDataSource;
|
import com.zaxxer.hikari.HikariDataSource;
|
||||||
import com.zaxxer.hikari.HikariPoolMXBean;
|
import com.zaxxer.hikari.HikariPoolMXBean;
|
||||||
|
|
@ -76,6 +77,7 @@ import java.util.stream.IntStream;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static com.appsmith.external.constants.ActionConstants.ACTION_CONFIGURATION_BODY;
|
import static com.appsmith.external.constants.ActionConstants.ACTION_CONFIGURATION_BODY;
|
||||||
|
import static com.appsmith.external.constants.PluginConstants.PluginName.POSTGRES_PLUGIN_NAME;
|
||||||
import static com.appsmith.external.helpers.PluginUtils.getColumnsListForJdbcPlugin;
|
import static com.appsmith.external.helpers.PluginUtils.getColumnsListForJdbcPlugin;
|
||||||
import static com.appsmith.external.helpers.PluginUtils.getIdenticalColumns;
|
import static com.appsmith.external.helpers.PluginUtils.getIdenticalColumns;
|
||||||
import static com.appsmith.external.helpers.PluginUtils.getPSParamLabel;
|
import static com.appsmith.external.helpers.PluginUtils.getPSParamLabel;
|
||||||
|
|
@ -124,13 +126,14 @@ public class PostgresPlugin extends BasePlugin {
|
||||||
|
|
||||||
private static int MAX_SIZE_SUPPORTED;
|
private static int MAX_SIZE_SUPPORTED;
|
||||||
|
|
||||||
|
public static PostgresDatasourceUtils postgresDatasourceUtils = new PostgresDatasourceUtils();
|
||||||
|
|
||||||
public PostgresPlugin(PluginWrapper wrapper) {
|
public PostgresPlugin(PluginWrapper wrapper) {
|
||||||
super(wrapper);
|
super(wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Extension
|
@Extension
|
||||||
public static class PostgresPluginExecutor implements SmartSubstitutionInterface, PluginExecutor<HikariDataSource> {
|
public static class PostgresPluginExecutor implements SmartSubstitutionInterface, PluginExecutor<HikariDataSource> {
|
||||||
|
|
||||||
private final Scheduler scheduler = Schedulers.boundedElastic();
|
private final Scheduler scheduler = Schedulers.boundedElastic();
|
||||||
|
|
||||||
private static final String TABLES_QUERY = "select a.attname as name,\n"
|
private static final String TABLES_QUERY = "select a.attname as name,\n"
|
||||||
|
|
@ -281,7 +284,8 @@ public class PostgresPlugin extends BasePlugin {
|
||||||
Connection connectionFromPool;
|
Connection connectionFromPool;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
connectionFromPool = getConnectionFromConnectionPool(connection, datasourceConfiguration);
|
connectionFromPool = postgresDatasourceUtils.getConnectionFromHikariConnectionPool(connection,
|
||||||
|
POSTGRES_PLUGIN_NAME);
|
||||||
} catch (SQLException | StaleConnectionException e) {
|
} catch (SQLException | StaleConnectionException e) {
|
||||||
// The function can throw either StaleConnectionException or SQLException. The
|
// The function can throw either StaleConnectionException or SQLException. The
|
||||||
// underlying hikari
|
// underlying hikari
|
||||||
|
|
@ -290,7 +294,7 @@ public class PostgresPlugin extends BasePlugin {
|
||||||
// the connection pool which can also be translated in our world to
|
// the connection pool which can also be translated in our world to
|
||||||
// StaleConnectionException
|
// StaleConnectionException
|
||||||
// and should then trigger the destruction and recreation of the pool.
|
// and should then trigger the destruction and recreation of the pool.
|
||||||
return Mono.error(e instanceof StaleConnectionException ? e : new StaleConnectionException());
|
return Mono.error(e instanceof StaleConnectionException ? e : new StaleConnectionException(e.getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Map<String, Object>> rowsList = new ArrayList<>(50);
|
List<Map<String, Object>> rowsList = new ArrayList<>(50);
|
||||||
|
|
@ -631,7 +635,8 @@ public class PostgresPlugin extends BasePlugin {
|
||||||
|
|
||||||
Connection connectionFromPool;
|
Connection connectionFromPool;
|
||||||
try {
|
try {
|
||||||
connectionFromPool = getConnectionFromConnectionPool(connection, datasourceConfiguration);
|
connectionFromPool = postgresDatasourceUtils.getConnectionFromHikariConnectionPool(connection,
|
||||||
|
POSTGRES_PLUGIN_NAME);
|
||||||
} catch (SQLException | StaleConnectionException e) {
|
} catch (SQLException | StaleConnectionException e) {
|
||||||
// The function can throw either StaleConnectionException or SQLException. The
|
// The function can throw either StaleConnectionException or SQLException. The
|
||||||
// underlying hikari
|
// underlying hikari
|
||||||
|
|
@ -640,7 +645,8 @@ public class PostgresPlugin extends BasePlugin {
|
||||||
// the connection pool which can also be translated in our world to
|
// the connection pool which can also be translated in our world to
|
||||||
// StaleConnectionException
|
// StaleConnectionException
|
||||||
// and should then trigger the destruction and recreation of the pool.
|
// and should then trigger the destruction and recreation of the pool.
|
||||||
return Mono.error(e instanceof StaleConnectionException ? e : new StaleConnectionException());
|
return Mono.error(e instanceof StaleConnectionException ? e :
|
||||||
|
new StaleConnectionException(e.getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
HikariPoolMXBean poolProxy = connection.getHikariPoolMXBean();
|
HikariPoolMXBean poolProxy = connection.getHikariPoolMXBean();
|
||||||
|
|
@ -1101,30 +1107,4 @@ public class PostgresPlugin extends BasePlugin {
|
||||||
|
|
||||||
return datasource;
|
return datasource;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* First checks if the connection pool is still valid. If yes, we fetch a
|
|
||||||
* connection from the pool and return
|
|
||||||
* In case a connection is not available in the pool, SQL Exception is thrown
|
|
||||||
*
|
|
||||||
* @param connectionPool
|
|
||||||
* @return SQL Connection
|
|
||||||
*/
|
|
||||||
private static Connection getConnectionFromConnectionPool(HikariDataSource connectionPool,
|
|
||||||
DatasourceConfiguration datasourceConfiguration) throws SQLException {
|
|
||||||
|
|
||||||
if (connectionPool == null || connectionPool.isClosed() || !connectionPool.isRunning()) {
|
|
||||||
log.debug("Encountered stale connection pool in Postgres plugin. Reporting back.");
|
|
||||||
throw new StaleConnectionException();
|
|
||||||
}
|
|
||||||
|
|
||||||
Connection connection = connectionPool.getConnection();
|
|
||||||
|
|
||||||
com.appsmith.external.models.Connection configurationConnection = datasourceConfiguration.getConnection();
|
|
||||||
if (configurationConnection == null) {
|
|
||||||
return connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
return connection;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
package com.external.plugins.exceptions;
|
package com.external.plugins.exceptions;
|
||||||
|
|
||||||
public class PostgresErrorMessages {
|
import com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages;
|
||||||
private PostgresErrorMessages() {
|
import lombok.AccessLevel;
|
||||||
//Prevents instantiation
|
import lombok.NoArgsConstructor;
|
||||||
}
|
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE) // To prevent instantiation
|
||||||
|
public class PostgresErrorMessages extends BasePluginErrorMessages {
|
||||||
public static final String MISSING_QUERY_ERROR_MSG = "Missing required parameter: Query.";
|
public static final String MISSING_QUERY_ERROR_MSG = "Missing required parameter: Query.";
|
||||||
|
|
||||||
public static final String QUERY_EXECUTION_FAILED_ERROR_MSG = "Your PostgreSQL query failed to execute.";
|
public static final String QUERY_EXECUTION_FAILED_ERROR_MSG = "Your PostgreSQL query failed to execute.";
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
package com.external.plugins.utils;
|
||||||
|
|
||||||
|
import com.appsmith.external.exceptions.pluginExceptions.StaleConnectionException;
|
||||||
|
import com.zaxxer.hikari.HikariDataSource;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.CONNECTION_POOL_CLOSED_ERROR_MSG;
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.CONNECTION_POOL_NOT_RUNNING_ERROR_MSG;
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.CONNECTION_POOL_NULL_ERROR_MSG;
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.UNKNOWN_CONNECTION_ERROR_MSG;
|
||||||
|
|
||||||
|
public class PostgresDatasourceUtils {
|
||||||
|
public void checkHikariCPConnectionPoolValidity(HikariDataSource connectionPool, String pluginName) throws StaleConnectionException {
|
||||||
|
if (connectionPool == null || connectionPool.isClosed() || !connectionPool.isRunning()) {
|
||||||
|
String printMessage = MessageFormat.format(Thread.currentThread().getName() +
|
||||||
|
": Encountered stale connection pool in {0} plugin. Reporting back.", pluginName);
|
||||||
|
System.out.println(printMessage);
|
||||||
|
|
||||||
|
if (connectionPool == null) {
|
||||||
|
throw new StaleConnectionException(CONNECTION_POOL_NULL_ERROR_MSG);
|
||||||
|
}
|
||||||
|
else if (connectionPool.isClosed()) {
|
||||||
|
throw new StaleConnectionException(CONNECTION_POOL_CLOSED_ERROR_MSG);
|
||||||
|
}
|
||||||
|
else if (!connectionPool.isRunning()) {
|
||||||
|
throw new StaleConnectionException(CONNECTION_POOL_NOT_RUNNING_ERROR_MSG);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/**
|
||||||
|
* Ideally, code flow is never expected to reach here. However, this section has been added to catch
|
||||||
|
* those cases wherein a developer updates the parent if condition but does not update the nested
|
||||||
|
* if else conditions.
|
||||||
|
*/
|
||||||
|
throw new StaleConnectionException(UNKNOWN_CONNECTION_ERROR_MSG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Connection getConnectionFromHikariConnectionPool(HikariDataSource connectionPool,
|
||||||
|
String pluginName) throws SQLException {
|
||||||
|
checkHikariCPConnectionPoolValidity(connectionPool, pluginName);
|
||||||
|
return connectionPool.getConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
package com.external.plugins.exceptions;
|
package com.external.plugins.exceptions;
|
||||||
|
|
||||||
public class RedisErrorMessages {
|
import com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages;
|
||||||
private RedisErrorMessages() {
|
import lombok.AccessLevel;
|
||||||
//Prevents instantiation
|
import lombok.NoArgsConstructor;
|
||||||
}
|
|
||||||
|
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE) // To prevent instantiation
|
||||||
|
public class RedisErrorMessages extends BasePluginErrorMessages {
|
||||||
public static final String BODY_IS_NULL_OR_EMPTY_ERROR_MSG = "Body is null or empty [%s]";
|
public static final String BODY_IS_NULL_OR_EMPTY_ERROR_MSG = "Body is null or empty [%s]";
|
||||||
|
|
||||||
public static final String QUERY_PARSING_FAILED_ERROR_MSG = "Appsmith server has failed to parse your Redis query. Are you sure it's" +
|
public static final String QUERY_PARSING_FAILED_ERROR_MSG = "Appsmith server has failed to parse your Redis query. Are you sure it's" +
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@
|
||||||
<groupId>com.amazon.redshift</groupId>
|
<groupId>com.amazon.redshift</groupId>
|
||||||
<artifactId>redshift-jdbc42</artifactId>
|
<artifactId>redshift-jdbc42</artifactId>
|
||||||
<version>2.1.0.9</version>
|
<version>2.1.0.9</version>
|
||||||
<scope>runtime</scope>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.zaxxer</groupId>
|
<groupId>com.zaxxer</groupId>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import com.appsmith.external.plugins.BasePlugin;
|
||||||
import com.appsmith.external.plugins.PluginExecutor;
|
import com.appsmith.external.plugins.PluginExecutor;
|
||||||
import com.external.plugins.exceptions.RedshiftErrorMessages;
|
import com.external.plugins.exceptions.RedshiftErrorMessages;
|
||||||
import com.external.plugins.exceptions.RedshiftPluginError;
|
import com.external.plugins.exceptions.RedshiftPluginError;
|
||||||
|
import com.external.utils.RedshiftDatasourceUtils;
|
||||||
import com.zaxxer.hikari.HikariDataSource;
|
import com.zaxxer.hikari.HikariDataSource;
|
||||||
import com.zaxxer.hikari.HikariPoolMXBean;
|
import com.zaxxer.hikari.HikariPoolMXBean;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
|
|
@ -47,15 +48,17 @@ import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static com.appsmith.external.constants.ActionConstants.ACTION_CONFIGURATION_BODY;
|
import static com.appsmith.external.constants.ActionConstants.ACTION_CONFIGURATION_BODY;
|
||||||
|
import static com.appsmith.external.constants.PluginConstants.PluginName.REDSHIFT_PLUGIN_NAME;
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.JDBC_DRIVER_LOADING_ERROR_MSG;
|
||||||
import static com.appsmith.external.helpers.PluginUtils.getColumnsListForJdbcPlugin;
|
import static com.appsmith.external.helpers.PluginUtils.getColumnsListForJdbcPlugin;
|
||||||
import static com.appsmith.external.helpers.PluginUtils.getIdenticalColumns;
|
import static com.appsmith.external.helpers.PluginUtils.getIdenticalColumns;
|
||||||
import static com.external.utils.RedshiftDatasourceUtils.createConnectionPool;
|
import static com.external.utils.RedshiftDatasourceUtils.createConnectionPool;
|
||||||
import static com.external.utils.RedshiftDatasourceUtils.getConnectionFromConnectionPool;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class RedshiftPlugin extends BasePlugin {
|
public class RedshiftPlugin extends BasePlugin {
|
||||||
public static final String JDBC_DRIVER = "com.amazon.redshift.jdbc.Driver";
|
public static final String JDBC_DRIVER = "com.amazon.redshift.jdbc.Driver";
|
||||||
private static final String DATE_COLUMN_TYPE_NAME = "date";
|
private static final String DATE_COLUMN_TYPE_NAME = "date";
|
||||||
|
public static RedshiftDatasourceUtils redshiftDatasourceUtils = new RedshiftDatasourceUtils();
|
||||||
|
|
||||||
public RedshiftPlugin(PluginWrapper wrapper) {
|
public RedshiftPlugin(PluginWrapper wrapper) {
|
||||||
super(wrapper);
|
super(wrapper);
|
||||||
|
|
@ -212,7 +215,7 @@ public class RedshiftPlugin extends BasePlugin {
|
||||||
return Mono.fromCallable(() -> {
|
return Mono.fromCallable(() -> {
|
||||||
Connection connection = null;
|
Connection connection = null;
|
||||||
try {
|
try {
|
||||||
connection = getConnectionFromConnectionPool(connectionPool);
|
connection = redshiftDatasourceUtils.getConnectionFromHikariConnectionPool(connectionPool, REDSHIFT_PLUGIN_NAME);
|
||||||
} catch (SQLException | StaleConnectionException e) {
|
} catch (SQLException | StaleConnectionException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|
||||||
|
|
@ -230,13 +233,13 @@ public class RedshiftPlugin extends BasePlugin {
|
||||||
// library throws SQLException in case the pool is closed or there is an issue initializing
|
// library throws SQLException in case the pool is closed or there is an issue initializing
|
||||||
// the connection pool which can also be translated in our world to StaleConnectionException
|
// the connection pool which can also be translated in our world to StaleConnectionException
|
||||||
// and should then trigger the destruction and recreation of the pool.
|
// and should then trigger the destruction and recreation of the pool.
|
||||||
return Mono.error(new StaleConnectionException());
|
return Mono.error(new StaleConnectionException(e.getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keeping this print statement post call to getConnectionFromConnectionPool because it checks for
|
* Keeping this print statement post call to getConnectionFromHikariConnectionPool because it
|
||||||
* stale connection pool.
|
* checks for stale connection pool.
|
||||||
*/
|
*/
|
||||||
printConnectionPoolStatus(connectionPool, false);
|
printConnectionPoolStatus(connectionPool, false);
|
||||||
|
|
||||||
|
|
@ -359,7 +362,7 @@ public class RedshiftPlugin extends BasePlugin {
|
||||||
try {
|
try {
|
||||||
Class.forName(JDBC_DRIVER);
|
Class.forName(JDBC_DRIVER);
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_DATASOURCE_ARGUMENT_ERROR, RedshiftErrorMessages.JDBC_DRIVER_LOADING_ERROR_MSG, e.getMessage()));
|
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_DATASOURCE_ARGUMENT_ERROR, JDBC_DRIVER_LOADING_ERROR_MSG, e.getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Mono
|
return Mono
|
||||||
|
|
@ -569,7 +572,8 @@ public class RedshiftPlugin extends BasePlugin {
|
||||||
return Mono.fromSupplier(() -> {
|
return Mono.fromSupplier(() -> {
|
||||||
Connection connection = null;
|
Connection connection = null;
|
||||||
try {
|
try {
|
||||||
connection = getConnectionFromConnectionPool(connectionPool);
|
connection = redshiftDatasourceUtils.getConnectionFromHikariConnectionPool(connectionPool
|
||||||
|
, REDSHIFT_PLUGIN_NAME);
|
||||||
} catch (SQLException | StaleConnectionException e) {
|
} catch (SQLException | StaleConnectionException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|
||||||
|
|
@ -587,7 +591,7 @@ public class RedshiftPlugin extends BasePlugin {
|
||||||
// library throws SQLException in case the pool is closed or there is an issue initializing
|
// library throws SQLException in case the pool is closed or there is an issue initializing
|
||||||
// the connection pool which can also be translated in our world to StaleConnectionException
|
// the connection pool which can also be translated in our world to StaleConnectionException
|
||||||
// and should then trigger the destruction and recreation of the pool.
|
// and should then trigger the destruction and recreation of the pool.
|
||||||
return Mono.error(new StaleConnectionException());
|
return Mono.error(new StaleConnectionException(e.getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
package com.external.plugins.exceptions;
|
package com.external.plugins.exceptions;
|
||||||
|
|
||||||
public class RedshiftErrorMessages {
|
import com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages;
|
||||||
private RedshiftErrorMessages() {
|
import lombok.AccessLevel;
|
||||||
//Prevents instantiation
|
import lombok.NoArgsConstructor;
|
||||||
}
|
|
||||||
|
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE) // To prevent instantiation
|
||||||
|
public class RedshiftErrorMessages extends BasePluginErrorMessages {
|
||||||
public static final String QUERY_PARAMETER_MISSING_ERROR_MSG = "Missing required parameter: Query.";
|
public static final String QUERY_PARAMETER_MISSING_ERROR_MSG = "Missing required parameter: Query.";
|
||||||
|
|
||||||
public static final String NULL_RESULTSET_ERROR_MSG = "Redshift driver failed to fetch result: resultSet is null.";
|
public static final String NULL_RESULTSET_ERROR_MSG = "Redshift driver failed to fetch result: resultSet is null.";
|
||||||
|
|
@ -18,7 +19,5 @@ public class RedshiftErrorMessages {
|
||||||
public static final String GET_STRUCTURE_ERROR_MSG = "Appsmith server has failed to fetch the structure of the database. "
|
public static final String GET_STRUCTURE_ERROR_MSG = "Appsmith server has failed to fetch the structure of the database. "
|
||||||
+ "Please check if the database credentials are valid and/or you have the required permissions.";
|
+ "Please check if the database credentials are valid and/or you have the required permissions.";
|
||||||
|
|
||||||
public static final String JDBC_DRIVER_LOADING_ERROR_MSG = "Error loading Redshift JDBC Driver class.";
|
|
||||||
|
|
||||||
public static final String CONNECTION_POOL_CREATION_FAILED_ERROR_MSG = "Exception occurred while creating connection pool. One or more arguments in the datasource configuration may be invalid. Please check your datasource configuration.";
|
public static final String CONNECTION_POOL_CREATION_FAILED_ERROR_MSG = "Exception occurred while creating connection pool. One or more arguments in the datasource configuration may be invalid. Please check your datasource configuration.";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,14 @@ import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.text.MessageFormat;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.CONNECTION_POOL_CLOSED_ERROR_MSG;
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.CONNECTION_POOL_NOT_RUNNING_ERROR_MSG;
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.CONNECTION_POOL_NULL_ERROR_MSG;
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.UNKNOWN_CONNECTION_ERROR_MSG;
|
||||||
import static com.external.plugins.RedshiftPlugin.JDBC_DRIVER;
|
import static com.external.plugins.RedshiftPlugin.JDBC_DRIVER;
|
||||||
|
|
||||||
public class RedshiftDatasourceUtils {
|
public class RedshiftDatasourceUtils {
|
||||||
|
|
@ -95,14 +100,35 @@ public class RedshiftDatasourceUtils {
|
||||||
return datasource;
|
return datasource;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Connection getConnectionFromConnectionPool(HikariDataSource connectionPool) throws SQLException {
|
public void checkHikariCPConnectionPoolValidity(HikariDataSource connectionPool, String pluginName) throws StaleConnectionException {
|
||||||
|
|
||||||
if (connectionPool == null || connectionPool.isClosed() || !connectionPool.isRunning()) {
|
if (connectionPool == null || connectionPool.isClosed() || !connectionPool.isRunning()) {
|
||||||
System.out.println(Thread.currentThread().getName() +
|
String printMessage = MessageFormat.format(Thread.currentThread().getName() +
|
||||||
": Encountered stale connection pool in Redshift plugin. Reporting back.");
|
": Encountered stale connection pool in {0} plugin. Reporting back.", pluginName);
|
||||||
throw new StaleConnectionException();
|
System.out.println(printMessage);
|
||||||
}
|
|
||||||
|
|
||||||
|
if (connectionPool == null) {
|
||||||
|
throw new StaleConnectionException(CONNECTION_POOL_NULL_ERROR_MSG);
|
||||||
|
}
|
||||||
|
else if (connectionPool.isClosed()) {
|
||||||
|
throw new StaleConnectionException(CONNECTION_POOL_CLOSED_ERROR_MSG);
|
||||||
|
}
|
||||||
|
else if (!connectionPool.isRunning()) {
|
||||||
|
throw new StaleConnectionException(CONNECTION_POOL_NOT_RUNNING_ERROR_MSG);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/**
|
||||||
|
* Ideally, code flow is never expected to reach here. However, this section has been added to catch
|
||||||
|
* those cases wherein a developer updates the parent if condition but does not update the nested
|
||||||
|
* if else conditions.
|
||||||
|
*/
|
||||||
|
throw new StaleConnectionException(UNKNOWN_CONNECTION_ERROR_MSG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Connection getConnectionFromHikariConnectionPool(HikariDataSource connectionPool,
|
||||||
|
String pluginName) throws SQLException {
|
||||||
|
checkHikariCPConnectionPoolValidity(connectionPool, pluginName);
|
||||||
return connectionPool.getConnection();
|
return connectionPool.getConnection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
package com.external.plugins.exceptions;
|
package com.external.plugins.exceptions;
|
||||||
|
|
||||||
public class RestApiErrorMessages {
|
import com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages;
|
||||||
private RestApiErrorMessages() {
|
import lombok.AccessLevel;
|
||||||
//Prevents instantiation
|
import lombok.NoArgsConstructor;
|
||||||
}
|
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE) // To prevent instantiation
|
||||||
|
public class RestApiErrorMessages extends BasePluginErrorMessages {
|
||||||
public static final String URI_SYNTAX_WRONG_ERROR_MSG = "Invalid value of URI.";
|
public static final String URI_SYNTAX_WRONG_ERROR_MSG = "Invalid value of URI.";
|
||||||
public static final String INVALID_CONTENT_TYPE_ERROR_MSG = "Invalid value for Content-Type.";
|
public static final String INVALID_CONTENT_TYPE_ERROR_MSG = "Invalid value for Content-Type.";
|
||||||
public static final String NO_HTTP_METHOD_ERROR_MSG = "HTTPMethod must be set.";
|
public static final String NO_HTTP_METHOD_ERROR_MSG = "HTTPMethod must be set.";
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
package com.external.plugins.exceptions;
|
package com.external.plugins.exceptions;
|
||||||
|
|
||||||
public class SaaSErrorMessages {
|
import com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages;
|
||||||
private SaaSErrorMessages() {
|
import lombok.AccessLevel;
|
||||||
//Prevents instantiation
|
import lombok.NoArgsConstructor;
|
||||||
}
|
|
||||||
|
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE) // To prevent instantiation
|
||||||
|
public class SaaSErrorMessages extends BasePluginErrorMessages {
|
||||||
public static final String MISSING_DATASOURCE_TEMPLATE_NAME_ERROR_MSG = "Missing template name for datasource";
|
public static final String MISSING_DATASOURCE_TEMPLATE_NAME_ERROR_MSG = "Missing template name for datasource";
|
||||||
|
|
||||||
public static final String MISSING_ACTION_TEMPLATE_NAME_ERROR_MSG = "Missing template name for action";
|
public static final String MISSING_ACTION_TEMPLATE_NAME_ERROR_MSG = "Missing template name for action";
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
package com.external.plugins.exceptions;
|
package com.external.plugins.exceptions;
|
||||||
|
|
||||||
public class SMTPErrorMessages {
|
import lombok.AccessLevel;
|
||||||
private SMTPErrorMessages() {
|
import lombok.NoArgsConstructor;
|
||||||
//Prevents instantiation
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE) // To prevent instantiation
|
||||||
|
public class SMTPErrorMessages {
|
||||||
public static final String RECIPIENT_ADDRESS_NOT_FOUND_ERROR_MSG = "Couldn't find a valid recipient address. Please check your action configuration.";
|
public static final String RECIPIENT_ADDRESS_NOT_FOUND_ERROR_MSG = "Couldn't find a valid recipient address. Please check your action configuration.";
|
||||||
|
|
||||||
public static final String SENDER_ADDRESS_NOT_FOUND_ERROR_MSG = "Couldn't find a valid sender address. Please check your action configuration.";
|
public static final String SENDER_ADDRESS_NOT_FOUND_ERROR_MSG = "Couldn't find a valid sender address. Please check your action configuration.";
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ import com.appsmith.external.models.DatasourceTestResult;
|
||||||
import com.appsmith.external.plugins.BasePlugin;
|
import com.appsmith.external.plugins.BasePlugin;
|
||||||
import com.appsmith.external.plugins.PluginExecutor;
|
import com.appsmith.external.plugins.PluginExecutor;
|
||||||
import com.external.plugins.exceptions.SnowflakeErrorMessages;
|
import com.external.plugins.exceptions.SnowflakeErrorMessages;
|
||||||
import com.external.plugins.exceptions.SnowflakePluginError;
|
|
||||||
import com.external.utils.SqlUtils;
|
import com.external.utils.SqlUtils;
|
||||||
import com.zaxxer.hikari.HikariConfig;
|
import com.zaxxer.hikari.HikariConfig;
|
||||||
import com.zaxxer.hikari.HikariDataSource;
|
import com.zaxxer.hikari.HikariDataSource;
|
||||||
|
|
@ -22,7 +21,6 @@ import com.zaxxer.hikari.pool.HikariPool;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.pf4j.Extension;
|
import org.pf4j.Extension;
|
||||||
import org.pf4j.PluginWrapper;
|
import org.pf4j.PluginWrapper;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.core.scheduler.Scheduler;
|
import reactor.core.scheduler.Scheduler;
|
||||||
|
|
@ -41,7 +39,9 @@ import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static com.appsmith.external.constants.PluginConstants.PluginName.SNOWFLAKE_PLUGIN_NAME;
|
||||||
import static com.external.utils.ExecutionUtils.getRowsFromQueryResult;
|
import static com.external.utils.ExecutionUtils.getRowsFromQueryResult;
|
||||||
|
import static com.external.utils.SnowflakeDatasourceUtils.getConnectionFromHikariConnectionPool;
|
||||||
import static com.external.utils.ValidationUtils.validateWarehouseDatabaseSchema;
|
import static com.external.utils.ValidationUtils.validateWarehouseDatabaseSchema;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
|
@ -83,12 +83,19 @@ public class SnowflakePlugin extends BasePlugin {
|
||||||
Connection connectionFromPool;
|
Connection connectionFromPool;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
connectionFromPool = getConnectionFromConnectionPool(connection);
|
/**
|
||||||
|
* The getConnectionFromHikariConnectionPool method used here is the duplicate of
|
||||||
|
* method defined in PluginUtils.java and not the same one. Please check the comment on
|
||||||
|
* the method definition to understand more.
|
||||||
|
*/
|
||||||
|
connectionFromPool =
|
||||||
|
getConnectionFromHikariConnectionPool(connection,
|
||||||
|
SNOWFLAKE_PLUGIN_NAME);
|
||||||
} catch (SQLException | StaleConnectionException e) {
|
} catch (SQLException | StaleConnectionException e) {
|
||||||
if (e instanceof StaleConnectionException) {
|
if (e instanceof StaleConnectionException) {
|
||||||
throw e;
|
throw e;
|
||||||
} else {
|
} else {
|
||||||
throw new StaleConnectionException();
|
throw new StaleConnectionException(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -275,7 +282,14 @@ public class SnowflakePlugin extends BasePlugin {
|
||||||
|
|
||||||
Connection connectionFromPool;
|
Connection connectionFromPool;
|
||||||
try {
|
try {
|
||||||
connectionFromPool = getConnectionFromConnectionPool(connectionPool);
|
/**
|
||||||
|
* The getConnectionFromHikariConnectionPool method used here is the duplicate of
|
||||||
|
* method defined in PluginUtils.java and not the same one. Please check the comment on
|
||||||
|
* the method definition to understand more.
|
||||||
|
*/
|
||||||
|
connectionFromPool =
|
||||||
|
getConnectionFromHikariConnectionPool(connectionPool,
|
||||||
|
SNOWFLAKE_PLUGIN_NAME);
|
||||||
return Mono.just(validateWarehouseDatabaseSchema(connectionFromPool));
|
return Mono.just(validateWarehouseDatabaseSchema(connectionFromPool));
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
// The function can throw either StaleConnectionException or SQLException. The underlying hikari
|
// The function can throw either StaleConnectionException or SQLException. The underlying hikari
|
||||||
|
|
@ -313,7 +327,13 @@ public class SnowflakePlugin extends BasePlugin {
|
||||||
|
|
||||||
Connection connectionFromPool;
|
Connection connectionFromPool;
|
||||||
try {
|
try {
|
||||||
connectionFromPool = getConnectionFromConnectionPool(connection);
|
/**
|
||||||
|
* The getConnectionFromHikariConnectionPool method used here is the duplicate of
|
||||||
|
* method defined in PluginUtils.java and not the same one. Please check the comment on
|
||||||
|
* the method definition to understand more.
|
||||||
|
*/
|
||||||
|
connectionFromPool =
|
||||||
|
getConnectionFromHikariConnectionPool(connection, SNOWFLAKE_PLUGIN_NAME);
|
||||||
} catch (SQLException | StaleConnectionException e) {
|
} catch (SQLException | StaleConnectionException e) {
|
||||||
// The function can throw either StaleConnectionException or SQLException. The underlying hikari
|
// The function can throw either StaleConnectionException or SQLException. The underlying hikari
|
||||||
// library throws SQLException in case the pool is closed or there is an issue initializing
|
// library throws SQLException in case the pool is closed or there is an issue initializing
|
||||||
|
|
@ -391,22 +411,5 @@ public class SnowflakePlugin extends BasePlugin {
|
||||||
})
|
})
|
||||||
.subscribeOn(scheduler);
|
.subscribeOn(scheduler);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* First checks if the connection pool is still valid. If yes, we fetch a connection from the pool and return
|
|
||||||
* In case a connection is not available in the pool, SQL Exception is thrown
|
|
||||||
*
|
|
||||||
* @param connectionPool
|
|
||||||
* @return SQL Connection
|
|
||||||
*/
|
|
||||||
private static Connection getConnectionFromConnectionPool(HikariDataSource connectionPool) throws SQLException {
|
|
||||||
|
|
||||||
if (connectionPool == null || connectionPool.isClosed() || !connectionPool.isRunning()) {
|
|
||||||
log.debug("Encountered stale connection pool in Snowflake plugin. Reporting back.");
|
|
||||||
throw new StaleConnectionException();
|
|
||||||
}
|
|
||||||
|
|
||||||
return connectionPool.getConnection();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,17 +1,15 @@
|
||||||
package com.external.plugins.exceptions;
|
package com.external.plugins.exceptions;
|
||||||
|
|
||||||
public class SnowflakeErrorMessages {
|
import com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages;
|
||||||
private SnowflakeErrorMessages() {
|
import lombok.AccessLevel;
|
||||||
//Prevents instantiation
|
import lombok.NoArgsConstructor;
|
||||||
}
|
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE) // To prevent instantiation
|
||||||
|
public class SnowflakeErrorMessages extends BasePluginErrorMessages {
|
||||||
public static final String MISSING_QUERY_ERROR_MSG = "Missing required parameter: Query.";
|
public static final String MISSING_QUERY_ERROR_MSG = "Missing required parameter: Query.";
|
||||||
|
|
||||||
public static final String DRIVER_NOT_FOUND_ERROR_MSG = "Snowflake driver not found. Please reach out to Appsmith support to resolve this issue.";
|
|
||||||
|
|
||||||
public static final String QUERY_EXECUTION_FAILED_ERROR_MSG = "Your query failed to execute. Please check more information in the error details.";
|
public static final String QUERY_EXECUTION_FAILED_ERROR_MSG = "Your query failed to execute. Please check more information in the error details.";
|
||||||
|
|
||||||
public static final String CONNECTION_CREATION_FAILED_ERROR_MSG = "Error occurred while connecting to Snowflake endpoint: %s";
|
|
||||||
|
|
||||||
public static final String UNABLE_TO_CREATE_CONNECTION_ERROR_MSG = "Unable to create connection to Snowflake URL";
|
public static final String UNABLE_TO_CREATE_CONNECTION_ERROR_MSG = "Unable to create connection to Snowflake URL";
|
||||||
|
|
||||||
public static final String GET_STRUCTURE_ERROR_MSG = "Appsmith server has failed to fetch the structure of your schema. Please check more information in the error details.";
|
public static final String GET_STRUCTURE_ERROR_MSG = "Appsmith server has failed to fetch the structure of your schema. Please check more information in the error details.";
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static com.external.plugins.exceptions.SnowflakeErrorMessages.CONNECTION_INVALID_ERROR_MSG;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ExecutionUtils {
|
public class ExecutionUtils {
|
||||||
/**
|
/**
|
||||||
|
|
@ -38,7 +40,7 @@ public class ExecutionUtils {
|
||||||
// Instead for every execution, we check for connection validity,
|
// Instead for every execution, we check for connection validity,
|
||||||
// and reset the connection if required
|
// and reset the connection if required
|
||||||
if (!connection.isValid(30)) {
|
if (!connection.isValid(30)) {
|
||||||
throw new StaleConnectionException();
|
throw new StaleConnectionException(CONNECTION_INVALID_ERROR_MSG);
|
||||||
}
|
}
|
||||||
|
|
||||||
statement = connection.createStatement();
|
statement = connection.createStatement();
|
||||||
|
|
@ -58,7 +60,7 @@ public class ExecutionUtils {
|
||||||
}
|
}
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
if (e instanceof SnowflakeReauthenticationRequest) {
|
if (e instanceof SnowflakeReauthenticationRequest) {
|
||||||
throw new StaleConnectionException();
|
throw new StaleConnectionException(e.getMessage());
|
||||||
}
|
}
|
||||||
log.error("Exception caught when executing Snowflake query. Cause: ", e);
|
log.error("Exception caught when executing Snowflake query. Cause: ", e);
|
||||||
throw new AppsmithPluginException(SnowflakePluginError.QUERY_EXECUTION_FAILED, SnowflakeErrorMessages.QUERY_EXECUTION_FAILED_ERROR_MSG, e.getMessage(), "SQLSTATE: " + e.getSQLState() );
|
throw new AppsmithPluginException(SnowflakePluginError.QUERY_EXECUTION_FAILED, SnowflakeErrorMessages.QUERY_EXECUTION_FAILED_ERROR_MSG, e.getMessage(), "SQLSTATE: " + e.getSQLState() );
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
package com.external.utils;
|
||||||
|
|
||||||
|
import com.appsmith.external.exceptions.pluginExceptions.StaleConnectionException;
|
||||||
|
import com.zaxxer.hikari.HikariDataSource;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.CONNECTION_POOL_CLOSED_ERROR_MSG;
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.CONNECTION_POOL_NOT_RUNNING_ERROR_MSG;
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.CONNECTION_POOL_NULL_ERROR_MSG;
|
||||||
|
import static com.appsmith.external.exceptions.pluginExceptions.BasePluginErrorMessages.UNKNOWN_CONNECTION_ERROR_MSG;
|
||||||
|
|
||||||
|
public class SnowflakeDatasourceUtils {
|
||||||
|
public static void checkHikariCPConnectionPoolValidity(HikariDataSource connectionPool, String pluginName) throws StaleConnectionException {
|
||||||
|
if (connectionPool == null || connectionPool.isClosed() || !connectionPool.isRunning()) {
|
||||||
|
String printMessage = MessageFormat.format(Thread.currentThread().getName() +
|
||||||
|
": Encountered stale connection pool in {0} plugin. Reporting back.", pluginName);
|
||||||
|
System.out.println(printMessage);
|
||||||
|
|
||||||
|
if (connectionPool == null) {
|
||||||
|
throw new StaleConnectionException(CONNECTION_POOL_NULL_ERROR_MSG);
|
||||||
|
}
|
||||||
|
else if (connectionPool.isClosed()) {
|
||||||
|
throw new StaleConnectionException(CONNECTION_POOL_CLOSED_ERROR_MSG);
|
||||||
|
}
|
||||||
|
else if (!connectionPool.isRunning()) {
|
||||||
|
throw new StaleConnectionException(CONNECTION_POOL_NOT_RUNNING_ERROR_MSG);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/**
|
||||||
|
* Ideally, code flow is never expected to reach here. However, this section has been added to catch
|
||||||
|
* those cases wherein a developer updates the parent if condition but does not update the nested
|
||||||
|
* if else conditions.
|
||||||
|
*/
|
||||||
|
throw new StaleConnectionException(UNKNOWN_CONNECTION_ERROR_MSG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Connection getConnectionFromHikariConnectionPool(HikariDataSource connectionPool,
|
||||||
|
String pluginName) throws SQLException {
|
||||||
|
checkHikariCPConnectionPoolValidity(connectionPool, pluginName);
|
||||||
|
return connectionPool.getConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -644,7 +644,7 @@ public class ActionExecutionSolutionCEImpl implements ActionExecutionSolutionCE
|
||||||
return new AppsmithPluginException(AppsmithPluginError.PLUGIN_QUERY_TIMEOUT_ERROR,
|
return new AppsmithPluginException(AppsmithPluginError.PLUGIN_QUERY_TIMEOUT_ERROR,
|
||||||
actionDTO.getName(), timeoutDuration);
|
actionDTO.getName(), timeoutDuration);
|
||||||
} else if (error instanceof StaleConnectionException e) {
|
} else if (error instanceof StaleConnectionException e) {
|
||||||
return new AppsmithPluginException(AppsmithPluginError.STALE_CONNECTION_ERROR);
|
return new AppsmithPluginException(AppsmithPluginError.STALE_CONNECTION_ERROR, e.getMessage());
|
||||||
} else {
|
} else {
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user