diff --git a/sql/db2/flowable-patch/src/main/java/liquibase/database/core/DmDatabase.java b/sql/db2/flowable-patch/src/main/java/liquibase/database/core/DmDatabase.java new file mode 100644 index 000000000..fbc4c6bc1 --- /dev/null +++ b/sql/db2/flowable-patch/src/main/java/liquibase/database/core/DmDatabase.java @@ -0,0 +1,598 @@ +package liquibase.database.core; + +import java.lang.reflect.Method; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import liquibase.CatalogAndSchema; +import liquibase.Scope; +import liquibase.database.AbstractJdbcDatabase; +import liquibase.database.DatabaseConnection; +import liquibase.database.OfflineConnection; +import liquibase.database.jvm.JdbcConnection; +import liquibase.exception.DatabaseException; +import liquibase.exception.UnexpectedLiquibaseException; +import liquibase.exception.ValidationErrors; +import liquibase.executor.ExecutorService; +import liquibase.statement.DatabaseFunction; +import liquibase.statement.SequenceCurrentValueFunction; +import liquibase.statement.SequenceNextValueFunction; +import liquibase.statement.core.RawCallStatement; +import liquibase.statement.core.RawSqlStatement; +import liquibase.structure.DatabaseObject; +import liquibase.structure.core.Catalog; +import liquibase.structure.core.Index; +import liquibase.structure.core.PrimaryKey; +import liquibase.structure.core.Schema; +import liquibase.util.JdbcUtils; +import liquibase.util.StringUtil; + +public class DmDatabase extends AbstractJdbcDatabase { + private static final String PRODUCT_NAME = "DM DBMS"; + + @Override + protected String getDefaultDatabaseProductName() { + return PRODUCT_NAME; + } + + /** + * Is this AbstractDatabase subclass the correct one to use for the given connection. + * + * @param conn + */ + @Override + public boolean isCorrectDatabaseImplementation(DatabaseConnection conn) throws DatabaseException { + return PRODUCT_NAME.equalsIgnoreCase(conn.getDatabaseProductName()); + } + + /** + * If this database understands the given url, return the default driver class name. Otherwise return null. + * + * @param url + */ + @Override + public String getDefaultDriver(String url) { + if(url.startsWith("jdbc:dm")) { + return "dm.jdbc.driver.DmDriver"; + } + + return null; + } + + /** + * Returns an all-lower-case short name of the product. Used for end-user selecting of database type + * such as the DBMS precondition. + */ + @Override + public String getShortName() { + return "dm"; + } + + @Override + public Integer getDefaultPort() { + return 5236; + } + + /** + * Returns whether this database support initially deferrable columns. + */ + @Override + public boolean supportsInitiallyDeferrableColumns() { + return true; + } + + @Override + public boolean supportsTablespaces() { + return true; + } + + @Override + public int getPriority() { + return PRIORITY_DEFAULT; + } + + private static final Pattern PROXY_USER = Pattern.compile(".*(?:thin|oci)\\:(.+)/@.*"); + + protected final int SHORT_IDENTIFIERS_LENGTH = 30; + protected final int LONG_IDENTIFIERS_LEGNTH = 128; + public static final int ORACLE_12C_MAJOR_VERSION = 12; + + private Set reservedWords = new HashSet<>(); + private Set userDefinedTypes; + private Map savedSessionNlsSettings; + + private Boolean canAccessDbaRecycleBin; + private Integer databaseMajorVersion; + private Integer databaseMinorVersion; + + /** + * Default constructor for an object that represents the Oracle Database DBMS. + */ + public DmDatabase() { + super.unquotedObjectsAreUppercased = true; + //noinspection HardCodedStringLiteral + super.setCurrentDateTimeFunction("SYSTIMESTAMP"); + // Setting list of Oracle's native functions + //noinspection HardCodedStringLiteral + dateFunctions.add(new DatabaseFunction("SYSDATE")); + //noinspection HardCodedStringLiteral + dateFunctions.add(new DatabaseFunction("SYSTIMESTAMP")); + //noinspection HardCodedStringLiteral + dateFunctions.add(new DatabaseFunction("CURRENT_TIMESTAMP")); + //noinspection HardCodedStringLiteral + super.sequenceNextValueFunction = "%s.nextval"; + //noinspection HardCodedStringLiteral + super.sequenceCurrentValueFunction = "%s.currval"; + } + + private void tryProxySession(final String url, final Connection con) { + Matcher m = PROXY_USER.matcher(url); + if (m.matches()) { + Properties props = new Properties(); + props.put("PROXY_USER_NAME", m.group(1)); + try { + Method method = con.getClass().getMethod("openProxySession", int.class, Properties.class); + method.setAccessible(true); + method.invoke(con, 1, props); + } catch (Exception e) { + Scope.getCurrentScope().getLog(getClass()).info("Could not open proxy session on OracleDatabase: " + e.getCause().getMessage()); + } + } + } + + @Override + public int getDatabaseMajorVersion() throws DatabaseException { + if (databaseMajorVersion == null) { + return super.getDatabaseMajorVersion(); + } else { + return databaseMajorVersion; + } + } + + @Override + public int getDatabaseMinorVersion() throws DatabaseException { + if (databaseMinorVersion == null) { + return super.getDatabaseMinorVersion(); + } else { + return databaseMinorVersion; + } + } + + @Override + public String getJdbcCatalogName(CatalogAndSchema schema) { + return null; + } + + @Override + public String getJdbcSchemaName(CatalogAndSchema schema) { + return correctObjectName((schema.getCatalogName() == null) ? schema.getSchemaName() : schema.getCatalogName(), Schema.class); + } + + @Override + protected String getAutoIncrementClause(final String generationType, final Boolean defaultOnNull) { + if (StringUtil.isEmpty(generationType)) { + return super.getAutoIncrementClause(); + } + + String autoIncrementClause = "GENERATED %s AS IDENTITY"; // %s -- [ ALWAYS | BY DEFAULT [ ON NULL ] ] + String generationStrategy = generationType; + if (Boolean.TRUE.equals(defaultOnNull) && generationType.toUpperCase().equals("BY DEFAULT")) { + generationStrategy += " ON NULL"; + } + return String.format(autoIncrementClause, generationStrategy); + } + + @Override + public String generatePrimaryKeyName(String tableName) { + if (tableName.length() > 27) { + //noinspection HardCodedStringLiteral + return "PK_" + tableName.toUpperCase(Locale.US).substring(0, 27); + } else { + //noinspection HardCodedStringLiteral + return "PK_" + tableName.toUpperCase(Locale.US); + } + } + + @Override + public boolean isReservedWord(String objectName) { + return reservedWords.contains(objectName.toUpperCase()); + } + + @Override + public boolean supportsSequences() { + return true; + } + + /** + * Oracle supports catalogs in liquibase terms + * + * @return false + */ + @Override + public boolean supportsSchemas() { + return false; + } + + @Override + protected String getConnectionCatalogName() throws DatabaseException { + if (getConnection() instanceof OfflineConnection) { + return getConnection().getCatalog(); + } + try { + //noinspection HardCodedStringLiteral + return Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", this).queryForObject(new RawCallStatement("select sys_context( 'userenv', 'current_schema' ) from dual"), String.class); + } catch (Exception e) { + //noinspection HardCodedStringLiteral + Scope.getCurrentScope().getLog(getClass()).info("Error getting default schema", e); + } + return null; + } + + @Override + public String getDefaultCatalogName() {//NOPMD + return (super.getDefaultCatalogName() == null) ? null : super.getDefaultCatalogName().toUpperCase(Locale.US); + } + + /** + *

Returns an Oracle date literal with the same value as a string formatted using ISO 8601.

+ * + *

Convert an ISO8601 date string to one of the following results: + * to_date('1995-05-23', 'YYYY-MM-DD') + * to_date('1995-05-23 09:23:59', 'YYYY-MM-DD HH24:MI:SS')

+ *

+ * Implementation restriction:
+ * Currently, only the following subsets of ISO8601 are supported:
+ *

    + *
  • YYYY-MM-DD
  • + *
  • YYYY-MM-DDThh:mm:ss
  • + *
+ */ + @Override + public String getDateLiteral(String isoDate) { + String normalLiteral = super.getDateLiteral(isoDate); + + if (isDateOnly(isoDate)) { + return "TO_DATE(" + normalLiteral + ", 'YYYY-MM-DD')"; + } else if (isTimeOnly(isoDate)) { + return "TO_DATE(" + normalLiteral + ", 'HH24:MI:SS')"; + } else if (isTimestamp(isoDate)) { + return "TO_TIMESTAMP(" + normalLiteral + ", 'YYYY-MM-DD HH24:MI:SS.FF')"; + } else if (isDateTime(isoDate)) { + int seppos = normalLiteral.lastIndexOf('.'); + if (seppos != -1) { + normalLiteral = normalLiteral.substring(0, seppos) + "'"; + } + return "TO_DATE(" + normalLiteral + ", 'YYYY-MM-DD HH24:MI:SS')"; + } + return "UNSUPPORTED:" + isoDate; + } + + @Override + public boolean isSystemObject(DatabaseObject example) { + if (example == null) { + return false; + } + + if (this.isLiquibaseObject(example)) { + return false; + } + + if (example instanceof Schema) { + //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral + if ("SYSTEM".equals(example.getName()) || "SYS".equals(example.getName()) || "CTXSYS".equals(example.getName()) || "XDB".equals(example.getName())) { + return true; + } + //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral + if ("SYSTEM".equals(example.getSchema().getCatalogName()) || "SYS".equals(example.getSchema().getCatalogName()) || "CTXSYS".equals(example.getSchema().getCatalogName()) || "XDB".equals(example.getSchema().getCatalogName())) { + return true; + } + } else if (isSystemObject(example.getSchema())) { + return true; + } + if (example instanceof Catalog) { + //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral + if (("SYSTEM".equals(example.getName()) || "SYS".equals(example.getName()) || "CTXSYS".equals(example.getName()) || "XDB".equals(example.getName()))) { + return true; + } + } else if (example.getName() != null) { + //noinspection HardCodedStringLiteral + if (example.getName().startsWith("BIN$")) { //oracle deleted table + boolean filteredInOriginalQuery = this.canAccessDbaRecycleBin(); + if (!filteredInOriginalQuery) { + filteredInOriginalQuery = StringUtil.trimToEmpty(example.getSchema().getName()).equalsIgnoreCase(this.getConnection().getConnectionUserName()); + } + + if (filteredInOriginalQuery) { + return !((example instanceof PrimaryKey) || (example instanceof Index) || (example instanceof + liquibase.statement.UniqueConstraint)); + } else { + return true; + } + } else //noinspection HardCodedStringLiteral + if (example.getName().startsWith("AQ$")) { //oracle AQ tables + return true; + } else //noinspection HardCodedStringLiteral + if (example.getName().startsWith("DR$")) { //oracle index tables + return true; + } else //noinspection HardCodedStringLiteral + if (example.getName().startsWith("SYS_IOT_OVER")) { //oracle system table + return true; + } else //noinspection HardCodedStringLiteral,HardCodedStringLiteral + if ((example.getName().startsWith("MDRT_") || example.getName().startsWith("MDRS_")) && example.getName().endsWith("$")) { + // CORE-1768 - Oracle creates these for spatial indices and will remove them when the index is removed. + return true; + } else //noinspection HardCodedStringLiteral + if (example.getName().startsWith("MLOG$_")) { //Created by materliaized view logs for every table that is part of a materialized view. Not available for DDL operations. + return true; + } else //noinspection HardCodedStringLiteral + if (example.getName().startsWith("RUPD$_")) { //Created by materialized view log tables using primary keys. Not available for DDL operations. + return true; + } else //noinspection HardCodedStringLiteral + if (example.getName().startsWith("WM$_")) { //Workspace Manager backup tables. + return true; + } else //noinspection HardCodedStringLiteral + if ("CREATE$JAVA$LOB$TABLE".equals(example.getName())) { //This table contains the name of the Java object, the date it was loaded, and has a BLOB column to store the Java object. + return true; + } else //noinspection HardCodedStringLiteral + if ("JAVA$CLASS$MD5$TABLE".equals(example.getName())) { //This is a hash table that tracks the loading of Java objects into a schema. + return true; + } else //noinspection HardCodedStringLiteral + if (example.getName().startsWith("ISEQ$$_")) { //System-generated sequence + return true; + } else //noinspection HardCodedStringLiteral + if (example.getName().startsWith("USLOG$")) { //for update materialized view + return true; + } else if (example.getName().startsWith("SYS_FBA")) { //for Flashback tables + return true; + } + } + + return super.isSystemObject(example); + } + + @Override + public boolean supportsAutoIncrement() { + // Oracle supports Identity beginning with version 12c + boolean isAutoIncrementSupported = false; + + try { + if (getDatabaseMajorVersion() >= 12) { + isAutoIncrementSupported = true; + } + + // Returning true will generate create table command with 'IDENTITY' clause, example: + // CREATE TABLE AutoIncTest (IDPrimaryKey NUMBER(19) GENERATED BY DEFAULT AS IDENTITY NOT NULL, TypeID NUMBER(3) NOT NULL, Description NVARCHAR2(50), CONSTRAINT PK_AutoIncTest PRIMARY KEY (IDPrimaryKey)); + + // While returning false will continue to generate create table command without 'IDENTITY' clause, example: + // CREATE TABLE AutoIncTest (IDPrimaryKey NUMBER(19) NOT NULL, TypeID NUMBER(3) NOT NULL, Description NVARCHAR2(50), CONSTRAINT PK_AutoIncTest PRIMARY KEY (IDPrimaryKey)); + + } catch (DatabaseException ex) { + isAutoIncrementSupported = false; + } + + return isAutoIncrementSupported; + } + + +// public Set findUniqueConstraints(String schema) throws DatabaseException { +// Set returnSet = new HashSet(); +// +// List maps = new Executor(this).queryForList(new RawSqlStatement("SELECT UC.CONSTRAINT_NAME, UCC.TABLE_NAME, UCC.COLUMN_NAME FROM USER_CONSTRAINTS UC, USER_CONS_COLUMNS UCC WHERE UC.CONSTRAINT_NAME=UCC.CONSTRAINT_NAME AND CONSTRAINT_TYPE='U' ORDER BY UC.CONSTRAINT_NAME")); +// +// UniqueConstraint constraint = null; +// for (Map map : maps) { +// if (constraint == null || !constraint.getName().equals(constraint.getName())) { +// returnSet.add(constraint); +// Table table = new Table((String) map.get("TABLE_NAME")); +// constraint = new UniqueConstraint(map.get("CONSTRAINT_NAME").toString(), table); +// } +// } +// if (constraint != null) { +// returnSet.add(constraint); +// } +// +// return returnSet; +// } + + @Override + public boolean supportsRestrictForeignKeys() { + return false; + } + + @Override + public int getDataTypeMaxParameters(String dataTypeName) { + //noinspection HardCodedStringLiteral + if ("BINARY_FLOAT".equals(dataTypeName.toUpperCase())) { + return 0; + } + //noinspection HardCodedStringLiteral + if ("BINARY_DOUBLE".equals(dataTypeName.toUpperCase())) { + return 0; + } + return super.getDataTypeMaxParameters(dataTypeName); + } + + public String getSystemTableWhereClause(String tableNameColumn) { + List clauses = new ArrayList(Arrays.asList("BIN$", + "AQ$", + "DR$", + "SYS_IOT_OVER", + "MLOG$_", + "RUPD$_", + "WM$_", + "ISEQ$$_", + "USLOG$", + "SYS_FBA")); + + for (int i = 0;i getUserDefinedTypes() { + if (userDefinedTypes == null) { + userDefinedTypes = new HashSet<>(); + if ((getConnection() != null) && !(getConnection() instanceof OfflineConnection)) { + try { + try { + //noinspection HardCodedStringLiteral + userDefinedTypes.addAll(Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", this).queryForList(new RawSqlStatement("SELECT DISTINCT TYPE_NAME FROM ALL_TYPES"), String.class)); + } catch (DatabaseException e) { //fall back to USER_TYPES if the user cannot see ALL_TYPES + //noinspection HardCodedStringLiteral + userDefinedTypes.addAll(Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", this).queryForList(new RawSqlStatement("SELECT TYPE_NAME FROM USER_TYPES"), String.class)); + } + } catch (DatabaseException e) { + //ignore error + } + } + } + + return userDefinedTypes; + } + + @Override + public String generateDatabaseFunctionValue(DatabaseFunction databaseFunction) { + //noinspection HardCodedStringLiteral + if ((databaseFunction != null) && "current_timestamp".equalsIgnoreCase(databaseFunction.toString())) { + return databaseFunction.toString(); + } + if ((databaseFunction instanceof SequenceNextValueFunction) || (databaseFunction instanceof + SequenceCurrentValueFunction)) { + String quotedSeq = super.generateDatabaseFunctionValue(databaseFunction); + // replace "myschema.my_seq".nextval with "myschema"."my_seq".nextval + return quotedSeq.replaceFirst("\"([^\\.\"]+)\\.([^\\.\"]+)\"", "\"$1\".\"$2\""); + + } + + return super.generateDatabaseFunctionValue(databaseFunction); + } + + @Override + public ValidationErrors validate() { + ValidationErrors errors = super.validate(); + DatabaseConnection connection = getConnection(); + if ((connection == null) || (connection instanceof OfflineConnection)) { + //noinspection HardCodedStringLiteral + Scope.getCurrentScope().getLog(getClass()).info("Cannot validate offline database"); + return errors; + } + + if (!canAccessDbaRecycleBin()) { + errors.addWarning(getDbaRecycleBinWarning()); + } + + return errors; + + } + + public String getDbaRecycleBinWarning() { + //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral, + // HardCodedStringLiteral + //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral + return "Liquibase needs to access the DBA_RECYCLEBIN table so we can automatically handle the case where " + + "constraints are deleted and restored. Since Oracle doesn't properly restore the original table names " + + "referenced in the constraint, we use the information from the DBA_RECYCLEBIN to automatically correct this" + + " issue.\n" + + "\n" + + "The user you used to connect to the database (" + getConnection().getConnectionUserName() + + ") needs to have \"SELECT ON SYS.DBA_RECYCLEBIN\" permissions set before we can perform this operation. " + + "Please run the following SQL to set the appropriate permissions, and try running the command again.\n" + + "\n" + + " GRANT SELECT ON SYS.DBA_RECYCLEBIN TO " + getConnection().getConnectionUserName() + ";"; + } + + public boolean canAccessDbaRecycleBin() { + if (canAccessDbaRecycleBin == null) { + DatabaseConnection connection = getConnection(); + if ((connection == null) || (connection instanceof OfflineConnection)) { + return false; + } + + Statement statement = null; + try { + statement = ((JdbcConnection) connection).createStatement(); + @SuppressWarnings("HardCodedStringLiteral") ResultSet resultSet = statement.executeQuery("select 1 from dba_recyclebin where 0=1"); + resultSet.close(); //don't need to do anything with the result set, just make sure statement ran. + this.canAccessDbaRecycleBin = true; + } catch (Exception e) { + //noinspection HardCodedStringLiteral + if ((e instanceof SQLException) && e.getMessage().startsWith("ORA-00942")) { //ORA-00942: table or view does not exist + this.canAccessDbaRecycleBin = false; + } else { + //noinspection HardCodedStringLiteral + Scope.getCurrentScope().getLog(getClass()).warning("Cannot check dba_recyclebin access", e); + this.canAccessDbaRecycleBin = false; + } + } finally { + JdbcUtils.close(null, statement); + } + } + + return canAccessDbaRecycleBin; + } + + @Override + public boolean supportsNotNullConstraintNames() { + return true; + } + + /** + * Tests if the given String would be a valid identifier in Oracle DBMS. In Oracle, a valid identifier has + * the following form (case-insensitive comparison): + * 1st character: A-Z + * 2..n characters: A-Z0-9$_# + * The maximum length of an identifier differs by Oracle version and object type. + */ + public boolean isValidOracleIdentifier(String identifier, Class type) { + if ((identifier == null) || (identifier.length() < 1)) + return false; + + if (!identifier.matches("^(i?)[A-Z][A-Z0-9\\$\\_\\#]*$")) + return false; + + /* + * @todo It seems we currently do not have a class for tablespace identifiers, and all other classes + * we do know seem to be supported as 12cR2 long identifiers, so: + */ + return (identifier.length() <= LONG_IDENTIFIERS_LEGNTH); + } + + /** + * Returns the maximum number of bytes (NOT: characters) for an identifier. For Oracle <=12c Release 20, this + * is 30 bytes, and starting from 12cR2, up to 128 (except for tablespaces, PDB names and some other rather rare + * object types). + * + * @return the maximum length of an object identifier, in bytes + */ + public int getIdentifierMaximumLength() { + try { + if (getDatabaseMajorVersion() < ORACLE_12C_MAJOR_VERSION) { + return SHORT_IDENTIFIERS_LENGTH; + } else if ((getDatabaseMajorVersion() == ORACLE_12C_MAJOR_VERSION) && (getDatabaseMinorVersion() <= 1)) { + return SHORT_IDENTIFIERS_LENGTH; + } else { + return LONG_IDENTIFIERS_LEGNTH; + } + } catch (DatabaseException ex) { + throw new UnexpectedLiquibaseException("Cannot determine the Oracle database version number", ex); + } + + } +} diff --git a/sql/db2/flowable-patch/src/main/java/liquibase/datatype/core/BooleanType.java b/sql/db2/flowable-patch/src/main/java/liquibase/datatype/core/BooleanType.java new file mode 100644 index 000000000..cda2492e2 --- /dev/null +++ b/sql/db2/flowable-patch/src/main/java/liquibase/datatype/core/BooleanType.java @@ -0,0 +1,165 @@ +package liquibase.datatype.core; + +import liquibase.change.core.LoadDataChange; +import liquibase.database.Database; +import liquibase.database.core.*; +import liquibase.datatype.DataTypeInfo; +import liquibase.datatype.DatabaseDataType; +import liquibase.datatype.LiquibaseDataType; +import liquibase.exception.UnexpectedLiquibaseException; +import liquibase.statement.DatabaseFunction; +import liquibase.util.StringUtil; + +import java.util.Locale; +import java.util.regex.Pattern; + +@DataTypeInfo(name = "boolean", aliases = {"java.sql.Types.BOOLEAN", "java.lang.Boolean", "bit", "bool"}, minParameters = 0, maxParameters = 0, priority = LiquibaseDataType.PRIORITY_DEFAULT) +public class BooleanType extends LiquibaseDataType { + + @Override + public DatabaseDataType toDatabaseDataType(Database database) { + String originalDefinition = StringUtil.trimToEmpty(getRawDefinition()); + if ((database instanceof Firebird3Database)) { + return new DatabaseDataType("BOOLEAN"); + } + + if ((database instanceof Db2zDatabase) || (database instanceof FirebirdDatabase)) { + return new DatabaseDataType("SMALLINT"); + } else if (database instanceof MSSQLDatabase) { + return new DatabaseDataType(database.escapeDataTypeName("bit")); + } else if (database instanceof MySQLDatabase) { + if (originalDefinition.toLowerCase(Locale.US).startsWith("bit")) { + return new DatabaseDataType("BIT", getParameters()); + } + return new DatabaseDataType("BIT", 1); + } else if (database instanceof OracleDatabase) { + return new DatabaseDataType("NUMBER", 1); + } else if ((database instanceof SybaseASADatabase) || (database instanceof SybaseDatabase)) { + return new DatabaseDataType("BIT"); + } else if (database instanceof DerbyDatabase) { + if (((DerbyDatabase) database).supportsBooleanDataType()) { + return new DatabaseDataType("BOOLEAN"); + } else { + return new DatabaseDataType("SMALLINT"); + } + } else if (database instanceof DB2Database) { + if (((DB2Database) database).supportsBooleanDataType()) + return new DatabaseDataType("BOOLEAN"); + else + return new DatabaseDataType("SMALLINT"); + } else if (database instanceof HsqlDatabase) { + return new DatabaseDataType("BOOLEAN"); + } else if (database instanceof PostgresDatabase) { + if (originalDefinition.toLowerCase(Locale.US).startsWith("bit")) { + return new DatabaseDataType("BIT", getParameters()); + } + } else if (database instanceof DmDatabase) { // dhb52: DM Support + return new DatabaseDataType("bit"); + } + + return super.toDatabaseDataType(database); + } + + @Override + public String objectToSql(Object value, Database database) { + if ((value == null) || "null".equals(value.toString().toLowerCase(Locale.US))) { + return null; + } + + String returnValue; + if (value instanceof String) { + value = ((String) value).replaceAll("'", ""); + if ("true".equals(((String) value).toLowerCase(Locale.US)) || "1".equals(value) || "b'1'".equals(((String) value).toLowerCase(Locale.US)) || "t".equals(((String) value).toLowerCase(Locale.US)) || ((String) value).toLowerCase(Locale.US).equals(this.getTrueBooleanValue(database).toLowerCase(Locale.US))) { + returnValue = this.getTrueBooleanValue(database); + } else if ("false".equals(((String) value).toLowerCase(Locale.US)) || "0".equals(value) || "b'0'".equals( + ((String) value).toLowerCase(Locale.US)) || "f".equals(((String) value).toLowerCase(Locale.US)) || ((String) value).toLowerCase(Locale.US).equals(this.getFalseBooleanValue(database).toLowerCase(Locale.US))) { + returnValue = this.getFalseBooleanValue(database); + } else if (database instanceof PostgresDatabase && Pattern.matches("b?([01])\\1*(::bit|::\"bit\")?", (String) value)) { + returnValue = "b'" + + value.toString() + .replace("b", "") + .replace("\"", "") + .replace("::it", "") + + "'::\"bit\""; + } else { + throw new UnexpectedLiquibaseException("Unknown boolean value: " + value); + } + } else if (value instanceof Long) { + if (Long.valueOf(1).equals(value)) { + returnValue = this.getTrueBooleanValue(database); + } else { + returnValue = this.getFalseBooleanValue(database); + } + } else if (value instanceof Number) { + if (value.equals(1) || "1".equals(value.toString()) || "1.0".equals(value.toString())) { + returnValue = this.getTrueBooleanValue(database); + } else { + returnValue = this.getFalseBooleanValue(database); + } + } else if (value instanceof DatabaseFunction) { + return value.toString(); + } else if (value instanceof Boolean) { + if (((Boolean) value)) { + returnValue = this.getTrueBooleanValue(database); + } else { + returnValue = this.getFalseBooleanValue(database); + } + } else { + throw new UnexpectedLiquibaseException("Cannot convert type " + value.getClass() + " to a boolean value"); + } + + return returnValue; + } + + protected boolean isNumericBoolean(Database database) { + if (database instanceof Firebird3Database) { + return false; + } + if (database instanceof DerbyDatabase) { + return !((DerbyDatabase) database).supportsBooleanDataType(); + } else if (database instanceof DB2Database) { + return !((DB2Database) database).supportsBooleanDataType(); + } + return (database instanceof Db2zDatabase) + || (database instanceof FirebirdDatabase) + || (database instanceof MSSQLDatabase) + || (database instanceof MySQLDatabase) + || (database instanceof OracleDatabase) + || (database instanceof SQLiteDatabase) + || (database instanceof SybaseASADatabase) + || (database instanceof SybaseDatabase) + || (database instanceof DmDatabase); // dhb52: DM Support + } + + /** + * The database-specific value to use for "false" "boolean" columns. + */ + public String getFalseBooleanValue(Database database) { + if (isNumericBoolean(database)) { + return "0"; + } + if (database instanceof InformixDatabase) { + return "'f'"; + } + return "FALSE"; + } + + /** + * The database-specific value to use for "true" "boolean" columns. + */ + public String getTrueBooleanValue(Database database) { + if (isNumericBoolean(database)) { + return "1"; + } + if (database instanceof InformixDatabase) { + return "'t'"; + } + return "TRUE"; + } + + @Override + public LoadDataChange.LOAD_DATA_TYPE getLoadTypeName() { + return LoadDataChange.LOAD_DATA_TYPE.BOOLEAN; + } + +} diff --git a/sql/db2/flowable-patch/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java b/sql/db2/flowable-patch/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java new file mode 100644 index 000000000..33c52d551 --- /dev/null +++ b/sql/db2/flowable-patch/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java @@ -0,0 +1,2068 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.flowable.common.engine.impl; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.ServiceLoader; +import java.util.Set; + +import javax.naming.InitialContext; +import javax.sql.DataSource; + +import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.builder.xml.XMLConfigBuilder; +import org.apache.ibatis.builder.xml.XMLMapperBuilder; +import org.apache.ibatis.datasource.pooled.PooledDataSource; +import org.apache.ibatis.mapping.Environment; +import org.apache.ibatis.plugin.Interceptor; +import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.session.SqlSessionFactory; +import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory; +import org.apache.ibatis.transaction.TransactionFactory; +import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; +import org.apache.ibatis.transaction.managed.ManagedTransactionFactory; +import org.apache.ibatis.type.ArrayTypeHandler; +import org.apache.ibatis.type.BigDecimalTypeHandler; +import org.apache.ibatis.type.BlobInputStreamTypeHandler; +import org.apache.ibatis.type.BlobTypeHandler; +import org.apache.ibatis.type.BooleanTypeHandler; +import org.apache.ibatis.type.ByteTypeHandler; +import org.apache.ibatis.type.ClobTypeHandler; +import org.apache.ibatis.type.DateOnlyTypeHandler; +import org.apache.ibatis.type.DateTypeHandler; +import org.apache.ibatis.type.DoubleTypeHandler; +import org.apache.ibatis.type.FloatTypeHandler; +import org.apache.ibatis.type.IntegerTypeHandler; +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.LongTypeHandler; +import org.apache.ibatis.type.NClobTypeHandler; +import org.apache.ibatis.type.NStringTypeHandler; +import org.apache.ibatis.type.ShortTypeHandler; +import org.apache.ibatis.type.SqlxmlTypeHandler; +import org.apache.ibatis.type.StringTypeHandler; +import org.apache.ibatis.type.TimeOnlyTypeHandler; +import org.apache.ibatis.type.TypeHandlerRegistry; +import org.flowable.common.engine.api.FlowableException; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; +import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher; +import org.flowable.common.engine.api.delegate.event.FlowableEventListener; +import org.flowable.common.engine.api.engine.EngineLifecycleListener; +import org.flowable.common.engine.impl.agenda.AgendaOperationRunner; +import org.flowable.common.engine.impl.cfg.CommandExecutorImpl; +import org.flowable.common.engine.impl.cfg.IdGenerator; +import org.flowable.common.engine.impl.cfg.TransactionContextFactory; +import org.flowable.common.engine.impl.cfg.standalone.StandaloneMybatisTransactionContextFactory; +import org.flowable.common.engine.impl.db.CommonDbSchemaManager; +import org.flowable.common.engine.impl.db.DbSqlSessionFactory; +import org.flowable.common.engine.impl.db.LogSqlExecutionTimePlugin; +import org.flowable.common.engine.impl.db.MybatisTypeAliasConfigurator; +import org.flowable.common.engine.impl.db.MybatisTypeHandlerConfigurator; +import org.flowable.common.engine.impl.db.SchemaManager; +import org.flowable.common.engine.impl.event.EventDispatchAction; +import org.flowable.common.engine.impl.event.FlowableEventDispatcherImpl; +import org.flowable.common.engine.impl.interceptor.Command; +import org.flowable.common.engine.impl.interceptor.CommandConfig; +import org.flowable.common.engine.impl.interceptor.CommandContextFactory; +import org.flowable.common.engine.impl.interceptor.CommandContextInterceptor; +import org.flowable.common.engine.impl.interceptor.CommandExecutor; +import org.flowable.common.engine.impl.interceptor.CommandInterceptor; +import org.flowable.common.engine.impl.interceptor.CrDbRetryInterceptor; +import org.flowable.common.engine.impl.interceptor.DefaultCommandInvoker; +import org.flowable.common.engine.impl.interceptor.LogInterceptor; +import org.flowable.common.engine.impl.interceptor.SessionFactory; +import org.flowable.common.engine.impl.interceptor.TransactionContextInterceptor; +import org.flowable.common.engine.impl.lock.LockManager; +import org.flowable.common.engine.impl.lock.LockManagerImpl; +import org.flowable.common.engine.impl.logging.LoggingListener; +import org.flowable.common.engine.impl.logging.LoggingSession; +import org.flowable.common.engine.impl.logging.LoggingSessionFactory; +import org.flowable.common.engine.impl.persistence.GenericManagerFactory; +import org.flowable.common.engine.impl.persistence.StrongUuidGenerator; +import org.flowable.common.engine.impl.persistence.cache.EntityCache; +import org.flowable.common.engine.impl.persistence.cache.EntityCacheImpl; +import org.flowable.common.engine.impl.persistence.entity.ByteArrayEntityManager; +import org.flowable.common.engine.impl.persistence.entity.ByteArrayEntityManagerImpl; +import org.flowable.common.engine.impl.persistence.entity.Entity; +import org.flowable.common.engine.impl.persistence.entity.PropertyEntityManager; +import org.flowable.common.engine.impl.persistence.entity.PropertyEntityManagerImpl; +import org.flowable.common.engine.impl.persistence.entity.TableDataManager; +import org.flowable.common.engine.impl.persistence.entity.TableDataManagerImpl; +import org.flowable.common.engine.impl.persistence.entity.data.ByteArrayDataManager; +import org.flowable.common.engine.impl.persistence.entity.data.PropertyDataManager; +import org.flowable.common.engine.impl.persistence.entity.data.impl.MybatisByteArrayDataManager; +import org.flowable.common.engine.impl.persistence.entity.data.impl.MybatisPropertyDataManager; +import org.flowable.common.engine.impl.runtime.Clock; +import org.flowable.common.engine.impl.service.CommonEngineServiceImpl; +import org.flowable.common.engine.impl.util.DefaultClockImpl; +import org.flowable.common.engine.impl.util.IoUtil; +import org.flowable.common.engine.impl.util.ReflectUtil; +import org.flowable.eventregistry.api.EventRegistryEventConsumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +public abstract class AbstractEngineConfiguration { + + protected final Logger logger = LoggerFactory.getLogger(getClass()); + + /** The tenant id indicating 'no tenant' */ + public static final String NO_TENANT_ID = ""; + + /** + * Checks the version of the DB schema against the library when the form engine is being created and throws an exception if the versions don't match. + */ + public static final String DB_SCHEMA_UPDATE_FALSE = "false"; + public static final String DB_SCHEMA_UPDATE_CREATE = "create"; + public static final String DB_SCHEMA_UPDATE_CREATE_DROP = "create-drop"; + + /** + * Creates the schema when the form engine is being created and drops the schema when the form engine is being closed. + */ + public static final String DB_SCHEMA_UPDATE_DROP_CREATE = "drop-create"; + + /** + * Upon building of the process engine, a check is performed and an update of the schema is performed if it is necessary. + */ + public static final String DB_SCHEMA_UPDATE_TRUE = "true"; + + protected boolean forceCloseMybatisConnectionPool = true; + + protected String databaseType; + protected String jdbcDriver = "org.h2.Driver"; + protected String jdbcUrl = "jdbc:h2:tcp://localhost/~/flowable"; + protected String jdbcUsername = "sa"; + protected String jdbcPassword = ""; + protected String dataSourceJndiName; + protected int jdbcMaxActiveConnections = 16; + protected int jdbcMaxIdleConnections = 8; + protected int jdbcMaxCheckoutTime; + protected int jdbcMaxWaitTime; + protected boolean jdbcPingEnabled; + protected String jdbcPingQuery; + protected int jdbcPingConnectionNotUsedFor; + protected int jdbcDefaultTransactionIsolationLevel; + protected DataSource dataSource; + protected SchemaManager commonSchemaManager; + protected SchemaManager schemaManager; + protected Command schemaManagementCmd; + + protected String databaseSchemaUpdate = DB_SCHEMA_UPDATE_FALSE; + + /** + * Whether to use a lock when performing the database schema create or update operations. + */ + protected boolean useLockForDatabaseSchemaUpdate = false; + + protected String xmlEncoding = "UTF-8"; + + // COMMAND EXECUTORS /////////////////////////////////////////////// + + protected CommandExecutor commandExecutor; + protected Collection defaultCommandInterceptors; + protected CommandConfig defaultCommandConfig; + protected CommandConfig schemaCommandConfig; + protected CommandContextFactory commandContextFactory; + protected CommandInterceptor commandInvoker; + + protected AgendaOperationRunner agendaOperationRunner = (commandContext, runnable) -> runnable.run(); + + protected List customPreCommandInterceptors; + protected List customPostCommandInterceptors; + protected List commandInterceptors; + + protected Map engineConfigurations = new HashMap<>(); + protected Map serviceConfigurations = new HashMap<>(); + + protected ClassLoader classLoader; + /** + * Either use Class.forName or ClassLoader.loadClass for class loading. See http://forums.activiti.org/content/reflectutilloadclass-and-custom- classloader + */ + protected boolean useClassForNameClassLoading = true; + + protected List engineLifecycleListeners; + + // Event Registry ////////////////////////////////////////////////// + protected Map eventRegistryEventConsumers = new HashMap<>(); + + // MYBATIS SQL SESSION FACTORY ///////////////////////////////////// + + protected boolean isDbHistoryUsed = true; + protected DbSqlSessionFactory dbSqlSessionFactory; + protected SqlSessionFactory sqlSessionFactory; + protected TransactionFactory transactionFactory; + protected TransactionContextFactory transactionContextFactory; + + /** + * If set to true, enables bulk insert (grouping sql inserts together). Default true. + * For some databases (eg DB2+z/OS) needs to be set to false. + */ + protected boolean isBulkInsertEnabled = true; + + /** + * Some databases have a limit of how many parameters one sql insert can have (eg SQL Server, 2000 params (!= insert statements) ). Tweak this parameter in case of exceptions indicating too much + * is being put into one bulk insert, or make it higher if your database can cope with it and there are inserts with a huge amount of data. + *

+ * By default: 100 (55 for mssql server as it has a hard limit of 2000 parameters in a statement) + */ + protected int maxNrOfStatementsInBulkInsert = 100; + + public int DEFAULT_MAX_NR_OF_STATEMENTS_BULK_INSERT_SQL_SERVER = 55; // currently Execution has most params (35). 2000 / 35 = 57. + + protected String mybatisMappingFile; + protected Set> customMybatisMappers; + protected Set customMybatisXMLMappers; + protected List customMybatisInterceptors; + + protected Set dependentEngineMyBatisXmlMappers; + protected List dependentEngineMybatisTypeAliasConfigs; + protected List dependentEngineMybatisTypeHandlerConfigs; + + // SESSION FACTORIES /////////////////////////////////////////////// + protected List customSessionFactories; + protected Map, SessionFactory> sessionFactories; + + protected boolean enableEventDispatcher = true; + protected FlowableEventDispatcher eventDispatcher; + protected List eventListeners; + protected Map> typedEventListeners; + protected List additionalEventDispatchActions; + + protected LoggingListener loggingListener; + + protected boolean transactionsExternallyManaged; + + /** + * Flag that can be set to configure or not a relational database is used. This is useful for custom implementations that do not use relational databases at all. + * + * If true (default), the {@link AbstractEngineConfiguration#getDatabaseSchemaUpdate()} value will be used to determine what needs to happen wrt the database schema. + * + * If false, no validation or schema creation will be done. That means that the database schema must have been created 'manually' before but the engine does not validate whether the schema is + * correct. The {@link AbstractEngineConfiguration#getDatabaseSchemaUpdate()} value will not be used. + */ + protected boolean usingRelationalDatabase = true; + + /** + * Flag that can be set to configure whether or not a schema is used. This is useful for custom implementations that do not use relational databases at all. + * Setting {@link #usingRelationalDatabase} to true will automatically imply using a schema. + */ + protected boolean usingSchemaMgmt = true; + + /** + * Allows configuring a database table prefix which is used for all runtime operations of the process engine. For example, if you specify a prefix named 'PRE1.', Flowable will query for executions + * in a table named 'PRE1.ACT_RU_EXECUTION_'. + * + *

+ * NOTE: the prefix is not respected by automatic database schema management. If you use {@link AbstractEngineConfiguration#DB_SCHEMA_UPDATE_CREATE_DROP} or + * {@link AbstractEngineConfiguration#DB_SCHEMA_UPDATE_TRUE}, Flowable will create the database tables using the default names, regardless of the prefix configured here. + */ + protected String databaseTablePrefix = ""; + + /** + * Escape character for doing wildcard searches. + * + * This will be added at then end of queries that include for example a LIKE clause. For example: SELECT * FROM table WHERE column LIKE '%\%%' ESCAPE '\'; + */ + protected String databaseWildcardEscapeCharacter; + + /** + * database catalog to use + */ + protected String databaseCatalog = ""; + + /** + * In some situations you want to set the schema to use for table checks / generation if the database metadata doesn't return that correctly, see https://jira.codehaus.org/browse/ACT-1220, + * https://jira.codehaus.org/browse/ACT-1062 + */ + protected String databaseSchema; + + /** + * Set to true in case the defined databaseTablePrefix is a schema-name, instead of an actual table name prefix. This is relevant for checking if Flowable-tables exist, the databaseTablePrefix + * will not be used here - since the schema is taken into account already, adding a prefix for the table-check will result in wrong table-names. + */ + protected boolean tablePrefixIsSchema; + + /** + * Set to true if the latest version of a definition should be retrieved, ignoring a possible parent deployment id value + */ + protected boolean alwaysLookupLatestDefinitionVersion; + + /** + * Set to true if by default lookups should fallback to the default tenant (an empty string by default or a defined tenant value) + */ + protected boolean fallbackToDefaultTenant; + + /** + * Default tenant provider that is executed when looking up definitions, in case the global or local fallback to default tenant value is true + */ + protected DefaultTenantProvider defaultTenantProvider = (tenantId, scope, scopeKey) -> NO_TENANT_ID; + + /** + * Enables the MyBatis plugin that logs the execution time of sql statements. + */ + protected boolean enableLogSqlExecutionTime; + + protected Properties databaseTypeMappings = getDefaultDatabaseTypeMappings(); + + /** + * Duration between the checks when acquiring a lock. + */ + protected Duration lockPollRate = Duration.ofSeconds(10); + + /** + * Duration to wait for the DB Schema lock before giving up. + */ + protected Duration schemaLockWaitTime = Duration.ofMinutes(5); + + // DATA MANAGERS ////////////////////////////////////////////////////////////////// + + protected PropertyDataManager propertyDataManager; + protected ByteArrayDataManager byteArrayDataManager; + protected TableDataManager tableDataManager; + + // ENTITY MANAGERS //////////////////////////////////////////////////////////////// + + protected PropertyEntityManager propertyEntityManager; + protected ByteArrayEntityManager byteArrayEntityManager; + + protected List customPreDeployers; + protected List customPostDeployers; + protected List deployers; + + // CONFIGURATORS //////////////////////////////////////////////////////////// + + protected boolean enableConfiguratorServiceLoader = true; // Enabled by default. In certain environments this should be set to false (eg osgi) + protected List configurators; // The injected configurators + protected List allConfigurators; // Including auto-discovered configurators + protected EngineConfigurator idmEngineConfigurator; + protected EngineConfigurator eventRegistryConfigurator; + + public static final String PRODUCT_NAME_POSTGRES = "PostgreSQL"; + public static final String PRODUCT_NAME_CRDB = "CockroachDB"; + + public static final String DATABASE_TYPE_H2 = "h2"; + public static final String DATABASE_TYPE_HSQL = "hsql"; + public static final String DATABASE_TYPE_MYSQL = "mysql"; + public static final String DATABASE_TYPE_ORACLE = "oracle"; + public static final String DATABASE_TYPE_POSTGRES = "postgres"; + public static final String DATABASE_TYPE_MSSQL = "mssql"; + public static final String DATABASE_TYPE_DB2 = "db2"; + public static final String DATABASE_TYPE_COCKROACHDB = "cockroachdb"; + + public static Properties getDefaultDatabaseTypeMappings() { + Properties databaseTypeMappings = new Properties(); + databaseTypeMappings.setProperty("H2", DATABASE_TYPE_H2); + databaseTypeMappings.setProperty("HSQL Database Engine", DATABASE_TYPE_HSQL); + databaseTypeMappings.setProperty("MySQL", DATABASE_TYPE_MYSQL); + databaseTypeMappings.setProperty("MariaDB", DATABASE_TYPE_MYSQL); + databaseTypeMappings.setProperty("Oracle", DATABASE_TYPE_ORACLE); + databaseTypeMappings.setProperty(PRODUCT_NAME_POSTGRES, DATABASE_TYPE_POSTGRES); + databaseTypeMappings.setProperty("Microsoft SQL Server", DATABASE_TYPE_MSSQL); + databaseTypeMappings.setProperty(DATABASE_TYPE_DB2, DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/NT", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/NT64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2 UDP", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUX", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUX390", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUXX8664", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUXZ64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUXPPC64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUXPPC64LE", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/400 SQL", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/6000", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2 UDB iSeries", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/AIX64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/HPUX", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/HP64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/SUN", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/SUN64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/PTX", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/2", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2 UDB AS400", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty(PRODUCT_NAME_CRDB, DATABASE_TYPE_COCKROACHDB); + databaseTypeMappings.setProperty("DM DBMS", DATABASE_TYPE_ORACLE); // dhb52: DM support + return databaseTypeMappings; + } + + protected Map beans; + + protected IdGenerator idGenerator; + protected boolean usePrefixId; + + protected Clock clock; + protected ObjectMapper objectMapper; + + // Variables + + public static final int DEFAULT_GENERIC_MAX_LENGTH_STRING = 4000; + public static final int DEFAULT_ORACLE_MAX_LENGTH_STRING = 2000; + + /** + * Define a max length for storing String variable types in the database. Mainly used for the Oracle NVARCHAR2 limit of 2000 characters + */ + protected int maxLengthStringVariableType = -1; + + protected void initEngineConfigurations() { + addEngineConfiguration(getEngineCfgKey(), getEngineScopeType(), this); + } + + // DataSource + // /////////////////////////////////////////////////////////////// + + protected void initDataSource() { + if (dataSource == null) { + if (dataSourceJndiName != null) { + try { + dataSource = (DataSource) new InitialContext().lookup(dataSourceJndiName); + } catch (Exception e) { + throw new FlowableException("couldn't lookup datasource from " + dataSourceJndiName + ": " + e.getMessage(), e); + } + + } else if (jdbcUrl != null) { + if ((jdbcDriver == null) || (jdbcUsername == null)) { + throw new FlowableException("DataSource or JDBC properties have to be specified in a process engine configuration"); + } + + logger.debug("initializing datasource to db: {}", jdbcUrl); + + if (logger.isInfoEnabled()) { + logger.info("Configuring Datasource with following properties (omitted password for security)"); + logger.info("datasource driver : {}", jdbcDriver); + logger.info("datasource url : {}", jdbcUrl); + logger.info("datasource user name : {}", jdbcUsername); + } + + PooledDataSource pooledDataSource = new PooledDataSource(this.getClass().getClassLoader(), jdbcDriver, jdbcUrl, jdbcUsername, jdbcPassword); + + if (jdbcMaxActiveConnections > 0) { + pooledDataSource.setPoolMaximumActiveConnections(jdbcMaxActiveConnections); + } + if (jdbcMaxIdleConnections > 0) { + pooledDataSource.setPoolMaximumIdleConnections(jdbcMaxIdleConnections); + } + if (jdbcMaxCheckoutTime > 0) { + pooledDataSource.setPoolMaximumCheckoutTime(jdbcMaxCheckoutTime); + } + if (jdbcMaxWaitTime > 0) { + pooledDataSource.setPoolTimeToWait(jdbcMaxWaitTime); + } + if (jdbcPingEnabled) { + pooledDataSource.setPoolPingEnabled(true); + if (jdbcPingQuery != null) { + pooledDataSource.setPoolPingQuery(jdbcPingQuery); + } + pooledDataSource.setPoolPingConnectionsNotUsedFor(jdbcPingConnectionNotUsedFor); + } + if (jdbcDefaultTransactionIsolationLevel > 0) { + pooledDataSource.setDefaultTransactionIsolationLevel(jdbcDefaultTransactionIsolationLevel); + } + dataSource = pooledDataSource; + } + } + + if (databaseType == null) { + initDatabaseType(); + } + } + + public void initDatabaseType() { + Connection connection = null; + try { + connection = dataSource.getConnection(); + DatabaseMetaData databaseMetaData = connection.getMetaData(); + String databaseProductName = databaseMetaData.getDatabaseProductName(); + logger.debug("database product name: '{}'", databaseProductName); + + // CRDB does not expose the version through the jdbc driver, so we need to fetch it through version(). + if (PRODUCT_NAME_POSTGRES.equalsIgnoreCase(databaseProductName)) { + try (PreparedStatement preparedStatement = connection.prepareStatement("select version() as version;"); + ResultSet resultSet = preparedStatement.executeQuery()) { + String version = null; + if (resultSet.next()) { + version = resultSet.getString("version"); + } + + if (StringUtils.isNotEmpty(version) && version.toLowerCase().startsWith(PRODUCT_NAME_CRDB.toLowerCase())) { + databaseProductName = PRODUCT_NAME_CRDB; + logger.info("CockroachDB version '{}' detected", version); + } + } + } + + databaseType = databaseTypeMappings.getProperty(databaseProductName); + if (databaseType == null) { + throw new FlowableException("couldn't deduct database type from database product name '" + databaseProductName + "'"); + } + logger.debug("using database type: {}", databaseType); + + } catch (SQLException e) { + throw new RuntimeException("Exception while initializing Database connection", e); + } finally { + try { + if (connection != null) { + connection.close(); + } + } catch (SQLException e) { + logger.error("Exception while closing the Database connection", e); + } + } + + // Special care for MSSQL, as it has a hard limit of 2000 params per statement (incl bulk statement). + // Especially with executions, with 100 as default, this limit is passed. + if (DATABASE_TYPE_MSSQL.equals(databaseType)) { + maxNrOfStatementsInBulkInsert = DEFAULT_MAX_NR_OF_STATEMENTS_BULK_INSERT_SQL_SERVER; + } + } + + public void initSchemaManager() { + if (this.commonSchemaManager == null) { + this.commonSchemaManager = new CommonDbSchemaManager(); + } + } + + // session factories //////////////////////////////////////////////////////// + + public void addSessionFactory(SessionFactory sessionFactory) { + sessionFactories.put(sessionFactory.getSessionType(), sessionFactory); + } + + public void initCommandContextFactory() { + if (commandContextFactory == null) { + commandContextFactory = new CommandContextFactory(); + } + } + + public void initTransactionContextFactory() { + if (transactionContextFactory == null) { + transactionContextFactory = new StandaloneMybatisTransactionContextFactory(); + } + } + + public void initCommandExecutors() { + initDefaultCommandConfig(); + initSchemaCommandConfig(); + initCommandInvoker(); + initCommandInterceptors(); + initCommandExecutor(); + } + + + public void initDefaultCommandConfig() { + if (defaultCommandConfig == null) { + defaultCommandConfig = new CommandConfig(); + } + } + + public void initSchemaCommandConfig() { + if (schemaCommandConfig == null) { + schemaCommandConfig = new CommandConfig(); + } + } + + public void initCommandInvoker() { + if (commandInvoker == null) { + commandInvoker = new DefaultCommandInvoker(); + } + } + + public void initCommandInterceptors() { + if (commandInterceptors == null) { + commandInterceptors = new ArrayList<>(); + if (customPreCommandInterceptors != null) { + commandInterceptors.addAll(customPreCommandInterceptors); + } + commandInterceptors.addAll(getDefaultCommandInterceptors()); + if (customPostCommandInterceptors != null) { + commandInterceptors.addAll(customPostCommandInterceptors); + } + commandInterceptors.add(commandInvoker); + } + } + + public Collection getDefaultCommandInterceptors() { + if (defaultCommandInterceptors == null) { + List interceptors = new ArrayList<>(); + interceptors.add(new LogInterceptor()); + + if (DATABASE_TYPE_COCKROACHDB.equals(databaseType)) { + interceptors.add(new CrDbRetryInterceptor()); + } + + CommandInterceptor transactionInterceptor = createTransactionInterceptor(); + if (transactionInterceptor != null) { + interceptors.add(transactionInterceptor); + } + + if (commandContextFactory != null) { + String engineCfgKey = getEngineCfgKey(); + CommandContextInterceptor commandContextInterceptor = new CommandContextInterceptor(commandContextFactory, + classLoader, useClassForNameClassLoading, clock, objectMapper); + engineConfigurations.put(engineCfgKey, this); + commandContextInterceptor.setEngineCfgKey(engineCfgKey); + commandContextInterceptor.setEngineConfigurations(engineConfigurations); + interceptors.add(commandContextInterceptor); + } + + if (transactionContextFactory != null) { + interceptors.add(new TransactionContextInterceptor(transactionContextFactory)); + } + + List additionalCommandInterceptors = getAdditionalDefaultCommandInterceptors(); + if (additionalCommandInterceptors != null) { + interceptors.addAll(additionalCommandInterceptors); + } + + defaultCommandInterceptors = interceptors; + } + return defaultCommandInterceptors; + } + + public abstract String getEngineCfgKey(); + + public abstract String getEngineScopeType(); + + public List getAdditionalDefaultCommandInterceptors() { + return null; + } + + public void initCommandExecutor() { + if (commandExecutor == null) { + CommandInterceptor first = initInterceptorChain(commandInterceptors); + commandExecutor = new CommandExecutorImpl(getDefaultCommandConfig(), first); + } + } + + public CommandInterceptor initInterceptorChain(List chain) { + if (chain == null || chain.isEmpty()) { + throw new FlowableException("invalid command interceptor chain configuration: " + chain); + } + for (int i = 0; i < chain.size() - 1; i++) { + chain.get(i).setNext(chain.get(i + 1)); + } + return chain.get(0); + } + + public abstract CommandInterceptor createTransactionInterceptor(); + + + public void initBeans() { + if (beans == null) { + beans = new HashMap<>(); + } + } + + // id generator + // ///////////////////////////////////////////////////////////// + + public void initIdGenerator() { + if (idGenerator == null) { + idGenerator = new StrongUuidGenerator(); + } + } + + public void initObjectMapper() { + if (objectMapper == null) { + objectMapper = new ObjectMapper(); + objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + } + } + + public void initClock() { + if (clock == null) { + clock = new DefaultClockImpl(); + } + } + + // Data managers /////////////////////////////////////////////////////////// + + public void initDataManagers() { + if (propertyDataManager == null) { + propertyDataManager = new MybatisPropertyDataManager(idGenerator); + } + + if (byteArrayDataManager == null) { + byteArrayDataManager = new MybatisByteArrayDataManager(idGenerator); + } + } + + // Entity managers ////////////////////////////////////////////////////////// + + public void initEntityManagers() { + if (propertyEntityManager == null) { + propertyEntityManager = new PropertyEntityManagerImpl(this, propertyDataManager); + } + + if (byteArrayEntityManager == null) { + byteArrayEntityManager = new ByteArrayEntityManagerImpl(byteArrayDataManager, getEngineCfgKey(), this::getEventDispatcher); + } + + if (tableDataManager == null) { + tableDataManager = new TableDataManagerImpl(this); + } + } + + // services + // ///////////////////////////////////////////////////////////////// + + protected void initService(Object service) { + if (service instanceof CommonEngineServiceImpl) { + ((CommonEngineServiceImpl) service).setCommandExecutor(commandExecutor); + } + } + + // myBatis SqlSessionFactory + // //////////////////////////////////////////////// + + public void initSessionFactories() { + if (sessionFactories == null) { + sessionFactories = new HashMap<>(); + + if (usingRelationalDatabase) { + initDbSqlSessionFactory(); + } + + addSessionFactory(new GenericManagerFactory(EntityCache.class, EntityCacheImpl.class)); + + if (isLoggingSessionEnabled()) { + if (!sessionFactories.containsKey(LoggingSession.class)) { + LoggingSessionFactory loggingSessionFactory = new LoggingSessionFactory(); + loggingSessionFactory.setLoggingListener(loggingListener); + loggingSessionFactory.setObjectMapper(objectMapper); + sessionFactories.put(LoggingSession.class, loggingSessionFactory); + } + } + + commandContextFactory.setSessionFactories(sessionFactories); + + } else { + if (usingRelationalDatabase) { + initDbSqlSessionFactoryEntitySettings(); + } + } + + if (customSessionFactories != null) { + for (SessionFactory sessionFactory : customSessionFactories) { + addSessionFactory(sessionFactory); + } + } + } + + public void initDbSqlSessionFactory() { + if (dbSqlSessionFactory == null) { + dbSqlSessionFactory = createDbSqlSessionFactory(); + } + dbSqlSessionFactory.setDatabaseType(databaseType); + dbSqlSessionFactory.setSqlSessionFactory(sqlSessionFactory); + dbSqlSessionFactory.setDbHistoryUsed(isDbHistoryUsed); + dbSqlSessionFactory.setDatabaseTablePrefix(databaseTablePrefix); + dbSqlSessionFactory.setTablePrefixIsSchema(tablePrefixIsSchema); + dbSqlSessionFactory.setDatabaseCatalog(databaseCatalog); + dbSqlSessionFactory.setDatabaseSchema(databaseSchema); + dbSqlSessionFactory.setMaxNrOfStatementsInBulkInsert(maxNrOfStatementsInBulkInsert); + + initDbSqlSessionFactoryEntitySettings(); + + addSessionFactory(dbSqlSessionFactory); + } + + public DbSqlSessionFactory createDbSqlSessionFactory() { + return new DbSqlSessionFactory(usePrefixId); + } + + protected abstract void initDbSqlSessionFactoryEntitySettings(); + + protected void defaultInitDbSqlSessionFactoryEntitySettings(List> insertOrder, List> deleteOrder) { + if (insertOrder != null) { + for (Class clazz : insertOrder) { + dbSqlSessionFactory.getInsertionOrder().add(clazz); + + if (isBulkInsertEnabled) { + dbSqlSessionFactory.getBulkInserteableEntityClasses().add(clazz); + } + } + } + + if (deleteOrder != null) { + for (Class clazz : deleteOrder) { + dbSqlSessionFactory.getDeletionOrder().add(clazz); + } + } + } + + public void initTransactionFactory() { + if (transactionFactory == null) { + if (transactionsExternallyManaged) { + transactionFactory = new ManagedTransactionFactory(); + Properties properties = new Properties(); + properties.put("closeConnection", "false"); + this.transactionFactory.setProperties(properties); + } else { + transactionFactory = new JdbcTransactionFactory(); + } + } + } + + public void initSqlSessionFactory() { + if (sqlSessionFactory == null) { + InputStream inputStream = null; + try { + inputStream = getMyBatisXmlConfigurationStream(); + + Environment environment = new Environment("default", transactionFactory, dataSource); + Reader reader = new InputStreamReader(inputStream); + Properties properties = new Properties(); + properties.put("prefix", databaseTablePrefix); + + String wildcardEscapeClause = ""; + if ((databaseWildcardEscapeCharacter != null) && (databaseWildcardEscapeCharacter.length() != 0)) { + wildcardEscapeClause = " escape '" + databaseWildcardEscapeCharacter + "'"; + } + properties.put("wildcardEscapeClause", wildcardEscapeClause); + + // set default properties + properties.put("limitBefore", ""); + properties.put("limitAfter", ""); + properties.put("limitBetween", ""); + properties.put("limitBeforeNativeQuery", ""); + properties.put("limitAfterNativeQuery", ""); + properties.put("blobType", "BLOB"); + properties.put("boolValue", "TRUE"); + + if (databaseType != null) { + properties.load(getResourceAsStream(pathToEngineDbProperties())); + } + + Configuration configuration = initMybatisConfiguration(environment, reader, properties); + sqlSessionFactory = new DefaultSqlSessionFactory(configuration); + + } catch (Exception e) { + throw new FlowableException("Error while building ibatis SqlSessionFactory: " + e.getMessage(), e); + } finally { + IoUtil.closeSilently(inputStream); + } + } else { + // This is needed when the SQL Session Factory is created by another engine. + // When custom XML Mappers are registered with this engine they need to be loaded in the configuration as well + applyCustomMybatisCustomizations(sqlSessionFactory.getConfiguration()); + } + } + + public String pathToEngineDbProperties() { + return "org/flowable/common/db/properties/" + databaseType + ".properties"; + } + + public Configuration initMybatisConfiguration(Environment environment, Reader reader, Properties properties) { + XMLConfigBuilder parser = new XMLConfigBuilder(reader, "", properties); + Configuration configuration = parser.getConfiguration(); + + if (databaseType != null) { + configuration.setDatabaseId(databaseType); + } + + configuration.setEnvironment(environment); + + initMybatisTypeHandlers(configuration); + initCustomMybatisInterceptors(configuration); + if (isEnableLogSqlExecutionTime()) { + initMyBatisLogSqlExecutionTimePlugin(configuration); + } + + configuration = parseMybatisConfiguration(parser); + return configuration; + } + + public void initCustomMybatisMappers(Configuration configuration) { + if (getCustomMybatisMappers() != null) { + for (Class clazz : getCustomMybatisMappers()) { + if (!configuration.hasMapper(clazz)) { + configuration.addMapper(clazz); + } + } + } + } + + public void initMybatisTypeHandlers(Configuration configuration) { + // When mapping into Map there is currently a problem with MyBatis. + // It will return objects which are driver specific. + // Therefore we are registering the mappings between Object.class and the specific jdbc type here. + // see https://github.com/mybatis/mybatis-3/issues/2216 for more info + TypeHandlerRegistry handlerRegistry = configuration.getTypeHandlerRegistry(); + + handlerRegistry.register(Object.class, JdbcType.BOOLEAN, new BooleanTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.BIT, new BooleanTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.TINYINT, new ByteTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.SMALLINT, new ShortTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.INTEGER, new IntegerTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.FLOAT, new FloatTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.DOUBLE, new DoubleTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.CHAR, new StringTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.CLOB, new ClobTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.VARCHAR, new StringTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.LONGVARCHAR, new StringTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.NVARCHAR, new NStringTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.NCHAR, new NStringTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.NCLOB, new NClobTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.BIGINT, new LongTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.REAL, new BigDecimalTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.DECIMAL, new BigDecimalTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.NUMERIC, new BigDecimalTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.BLOB, new BlobInputStreamTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.LONGVARBINARY, new BlobTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.DATE, new DateOnlyTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.TIME, new TimeOnlyTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.TIMESTAMP, new DateTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.SQLXML, new SqlxmlTypeHandler()); + } + + public void initCustomMybatisInterceptors(Configuration configuration) { + if (customMybatisInterceptors!=null){ + for (Interceptor interceptor :customMybatisInterceptors){ + configuration.addInterceptor(interceptor); + } + } + } + + public void initMyBatisLogSqlExecutionTimePlugin(Configuration configuration) { + configuration.addInterceptor(new LogSqlExecutionTimePlugin()); + } + + public Configuration parseMybatisConfiguration(XMLConfigBuilder parser) { + Configuration configuration = parser.parse(); + + applyCustomMybatisCustomizations(configuration); + return configuration; + } + + protected void applyCustomMybatisCustomizations(Configuration configuration) { + initCustomMybatisMappers(configuration); + + if (dependentEngineMybatisTypeAliasConfigs != null) { + for (MybatisTypeAliasConfigurator typeAliasConfig : dependentEngineMybatisTypeAliasConfigs) { + typeAliasConfig.configure(configuration.getTypeAliasRegistry()); + } + } + if (dependentEngineMybatisTypeHandlerConfigs != null) { + for (MybatisTypeHandlerConfigurator typeHandlerConfig : dependentEngineMybatisTypeHandlerConfigs) { + typeHandlerConfig.configure(configuration.getTypeHandlerRegistry()); + } + } + + parseDependentEngineMybatisXMLMappers(configuration); + parseCustomMybatisXMLMappers(configuration); + } + + public void parseCustomMybatisXMLMappers(Configuration configuration) { + if (getCustomMybatisXMLMappers() != null) { + for (String resource : getCustomMybatisXMLMappers()) { + parseMybatisXmlMapping(configuration, resource); + } + } + } + + public void parseDependentEngineMybatisXMLMappers(Configuration configuration) { + if (getDependentEngineMyBatisXmlMappers() != null) { + for (String resource : getDependentEngineMyBatisXmlMappers()) { + parseMybatisXmlMapping(configuration, resource); + } + } + } + + protected void parseMybatisXmlMapping(Configuration configuration, String resource) { + // see XMLConfigBuilder.mapperElement() + XMLMapperBuilder mapperParser = new XMLMapperBuilder(getResourceAsStream(resource), configuration, resource, configuration.getSqlFragments()); + mapperParser.parse(); + } + + protected InputStream getResourceAsStream(String resource) { + ClassLoader classLoader = getClassLoader(); + if (classLoader != null) { + return getClassLoader().getResourceAsStream(resource); + } else { + return this.getClass().getClassLoader().getResourceAsStream(resource); + } + } + + public void setMybatisMappingFile(String file) { + this.mybatisMappingFile = file; + } + + public String getMybatisMappingFile() { + return mybatisMappingFile; + } + + public abstract InputStream getMyBatisXmlConfigurationStream(); + + public void initConfigurators() { + + allConfigurators = new ArrayList<>(); + allConfigurators.addAll(getEngineSpecificEngineConfigurators()); + + // Configurators that are explicitly added to the config + if (configurators != null) { + allConfigurators.addAll(configurators); + } + + // Auto discovery through ServiceLoader + if (enableConfiguratorServiceLoader) { + ClassLoader classLoader = getClassLoader(); + if (classLoader == null) { + classLoader = ReflectUtil.getClassLoader(); + } + + ServiceLoader configuratorServiceLoader = ServiceLoader.load(EngineConfigurator.class, classLoader); + int nrOfServiceLoadedConfigurators = 0; + for (EngineConfigurator configurator : configuratorServiceLoader) { + allConfigurators.add(configurator); + nrOfServiceLoadedConfigurators++; + } + + if (nrOfServiceLoadedConfigurators > 0) { + logger.info("Found {} auto-discoverable Process Engine Configurator{}", nrOfServiceLoadedConfigurators, nrOfServiceLoadedConfigurators > 1 ? "s" : ""); + } + + if (!allConfigurators.isEmpty()) { + + // Order them according to the priorities (useful for dependent + // configurator) + allConfigurators.sort(new Comparator() { + + @Override + public int compare(EngineConfigurator configurator1, EngineConfigurator configurator2) { + int priority1 = configurator1.getPriority(); + int priority2 = configurator2.getPriority(); + + if (priority1 < priority2) { + return -1; + } else if (priority1 > priority2) { + return 1; + } + return 0; + } + }); + + // Execute the configurators + logger.info("Found {} Engine Configurators in total:", allConfigurators.size()); + for (EngineConfigurator configurator : allConfigurators) { + logger.info("{} (priority:{})", configurator.getClass(), configurator.getPriority()); + } + + } + + } + } + + public void close() { + if (forceCloseMybatisConnectionPool && dataSource instanceof PooledDataSource) { + /* + * When the datasource is created by a Flowable engine (i.e. it's an instance of PooledDataSource), + * the connection pool needs to be closed when closing the engine. + * Note that calling forceCloseAll() multiple times (as is the case when running with multiple engine) is ok. + */ + ((PooledDataSource) dataSource).forceCloseAll(); + } + } + + protected List getEngineSpecificEngineConfigurators() { + // meant to be overridden if needed + return Collections.emptyList(); + } + + public void configuratorsBeforeInit() { + for (EngineConfigurator configurator : allConfigurators) { + logger.info("Executing beforeInit() of {} (priority:{})", configurator.getClass(), configurator.getPriority()); + configurator.beforeInit(this); + } + } + + public void configuratorsAfterInit() { + for (EngineConfigurator configurator : allConfigurators) { + logger.info("Executing configure() of {} (priority:{})", configurator.getClass(), configurator.getPriority()); + configurator.configure(this); + } + } + + public LockManager getLockManager(String lockName) { + return new LockManagerImpl(commandExecutor, lockName, getLockPollRate(), getEngineCfgKey()); + } + + // getters and setters + // ////////////////////////////////////////////////////// + + public abstract String getEngineName(); + + public ClassLoader getClassLoader() { + return classLoader; + } + + public AbstractEngineConfiguration setClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + return this; + } + + public boolean isUseClassForNameClassLoading() { + return useClassForNameClassLoading; + } + + public AbstractEngineConfiguration setUseClassForNameClassLoading(boolean useClassForNameClassLoading) { + this.useClassForNameClassLoading = useClassForNameClassLoading; + return this; + } + + public void addEngineLifecycleListener(EngineLifecycleListener engineLifecycleListener) { + if (this.engineLifecycleListeners == null) { + this.engineLifecycleListeners = new ArrayList<>(); + } + this.engineLifecycleListeners.add(engineLifecycleListener); + } + + public List getEngineLifecycleListeners() { + return engineLifecycleListeners; + } + + public AbstractEngineConfiguration setEngineLifecycleListeners(List engineLifecycleListeners) { + this.engineLifecycleListeners = engineLifecycleListeners; + return this; + } + + public String getDatabaseType() { + return databaseType; + } + + public AbstractEngineConfiguration setDatabaseType(String databaseType) { + this.databaseType = databaseType; + return this; + } + + public DataSource getDataSource() { + return dataSource; + } + + public AbstractEngineConfiguration setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + return this; + } + + public SchemaManager getSchemaManager() { + return schemaManager; + } + + public AbstractEngineConfiguration setSchemaManager(SchemaManager schemaManager) { + this.schemaManager = schemaManager; + return this; + } + + public SchemaManager getCommonSchemaManager() { + return commonSchemaManager; + } + + public AbstractEngineConfiguration setCommonSchemaManager(SchemaManager commonSchemaManager) { + this.commonSchemaManager = commonSchemaManager; + return this; + } + + public Command getSchemaManagementCmd() { + return schemaManagementCmd; + } + + public AbstractEngineConfiguration setSchemaManagementCmd(Command schemaManagementCmd) { + this.schemaManagementCmd = schemaManagementCmd; + return this; + } + + public String getJdbcDriver() { + return jdbcDriver; + } + + public AbstractEngineConfiguration setJdbcDriver(String jdbcDriver) { + this.jdbcDriver = jdbcDriver; + return this; + } + + public String getJdbcUrl() { + return jdbcUrl; + } + + public AbstractEngineConfiguration setJdbcUrl(String jdbcUrl) { + this.jdbcUrl = jdbcUrl; + return this; + } + + public String getJdbcUsername() { + return jdbcUsername; + } + + public AbstractEngineConfiguration setJdbcUsername(String jdbcUsername) { + this.jdbcUsername = jdbcUsername; + return this; + } + + public String getJdbcPassword() { + return jdbcPassword; + } + + public AbstractEngineConfiguration setJdbcPassword(String jdbcPassword) { + this.jdbcPassword = jdbcPassword; + return this; + } + + public int getJdbcMaxActiveConnections() { + return jdbcMaxActiveConnections; + } + + public AbstractEngineConfiguration setJdbcMaxActiveConnections(int jdbcMaxActiveConnections) { + this.jdbcMaxActiveConnections = jdbcMaxActiveConnections; + return this; + } + + public int getJdbcMaxIdleConnections() { + return jdbcMaxIdleConnections; + } + + public AbstractEngineConfiguration setJdbcMaxIdleConnections(int jdbcMaxIdleConnections) { + this.jdbcMaxIdleConnections = jdbcMaxIdleConnections; + return this; + } + + public int getJdbcMaxCheckoutTime() { + return jdbcMaxCheckoutTime; + } + + public AbstractEngineConfiguration setJdbcMaxCheckoutTime(int jdbcMaxCheckoutTime) { + this.jdbcMaxCheckoutTime = jdbcMaxCheckoutTime; + return this; + } + + public int getJdbcMaxWaitTime() { + return jdbcMaxWaitTime; + } + + public AbstractEngineConfiguration setJdbcMaxWaitTime(int jdbcMaxWaitTime) { + this.jdbcMaxWaitTime = jdbcMaxWaitTime; + return this; + } + + public boolean isJdbcPingEnabled() { + return jdbcPingEnabled; + } + + public AbstractEngineConfiguration setJdbcPingEnabled(boolean jdbcPingEnabled) { + this.jdbcPingEnabled = jdbcPingEnabled; + return this; + } + + public int getJdbcPingConnectionNotUsedFor() { + return jdbcPingConnectionNotUsedFor; + } + + public AbstractEngineConfiguration setJdbcPingConnectionNotUsedFor(int jdbcPingConnectionNotUsedFor) { + this.jdbcPingConnectionNotUsedFor = jdbcPingConnectionNotUsedFor; + return this; + } + + public int getJdbcDefaultTransactionIsolationLevel() { + return jdbcDefaultTransactionIsolationLevel; + } + + public AbstractEngineConfiguration setJdbcDefaultTransactionIsolationLevel(int jdbcDefaultTransactionIsolationLevel) { + this.jdbcDefaultTransactionIsolationLevel = jdbcDefaultTransactionIsolationLevel; + return this; + } + + public String getJdbcPingQuery() { + return jdbcPingQuery; + } + + public AbstractEngineConfiguration setJdbcPingQuery(String jdbcPingQuery) { + this.jdbcPingQuery = jdbcPingQuery; + return this; + } + + public String getDataSourceJndiName() { + return dataSourceJndiName; + } + + public AbstractEngineConfiguration setDataSourceJndiName(String dataSourceJndiName) { + this.dataSourceJndiName = dataSourceJndiName; + return this; + } + + public CommandConfig getSchemaCommandConfig() { + return schemaCommandConfig; + } + + public AbstractEngineConfiguration setSchemaCommandConfig(CommandConfig schemaCommandConfig) { + this.schemaCommandConfig = schemaCommandConfig; + return this; + } + + public boolean isTransactionsExternallyManaged() { + return transactionsExternallyManaged; + } + + public AbstractEngineConfiguration setTransactionsExternallyManaged(boolean transactionsExternallyManaged) { + this.transactionsExternallyManaged = transactionsExternallyManaged; + return this; + } + + public Map getBeans() { + return beans; + } + + public AbstractEngineConfiguration setBeans(Map beans) { + this.beans = beans; + return this; + } + + public IdGenerator getIdGenerator() { + return idGenerator; + } + + public AbstractEngineConfiguration setIdGenerator(IdGenerator idGenerator) { + this.idGenerator = idGenerator; + return this; + } + + public boolean isUsePrefixId() { + return usePrefixId; + } + + public AbstractEngineConfiguration setUsePrefixId(boolean usePrefixId) { + this.usePrefixId = usePrefixId; + return this; + } + + public String getXmlEncoding() { + return xmlEncoding; + } + + public AbstractEngineConfiguration setXmlEncoding(String xmlEncoding) { + this.xmlEncoding = xmlEncoding; + return this; + } + + public CommandConfig getDefaultCommandConfig() { + return defaultCommandConfig; + } + + public AbstractEngineConfiguration setDefaultCommandConfig(CommandConfig defaultCommandConfig) { + this.defaultCommandConfig = defaultCommandConfig; + return this; + } + + public CommandExecutor getCommandExecutor() { + return commandExecutor; + } + + public AbstractEngineConfiguration setCommandExecutor(CommandExecutor commandExecutor) { + this.commandExecutor = commandExecutor; + return this; + } + + public CommandContextFactory getCommandContextFactory() { + return commandContextFactory; + } + + public AbstractEngineConfiguration setCommandContextFactory(CommandContextFactory commandContextFactory) { + this.commandContextFactory = commandContextFactory; + return this; + } + + public CommandInterceptor getCommandInvoker() { + return commandInvoker; + } + + public AbstractEngineConfiguration setCommandInvoker(CommandInterceptor commandInvoker) { + this.commandInvoker = commandInvoker; + return this; + } + + public AgendaOperationRunner getAgendaOperationRunner() { + return agendaOperationRunner; + } + + public AbstractEngineConfiguration setAgendaOperationRunner(AgendaOperationRunner agendaOperationRunner) { + this.agendaOperationRunner = agendaOperationRunner; + return this; + } + + public List getCustomPreCommandInterceptors() { + return customPreCommandInterceptors; + } + + public AbstractEngineConfiguration setCustomPreCommandInterceptors(List customPreCommandInterceptors) { + this.customPreCommandInterceptors = customPreCommandInterceptors; + return this; + } + + public List getCustomPostCommandInterceptors() { + return customPostCommandInterceptors; + } + + public AbstractEngineConfiguration setCustomPostCommandInterceptors(List customPostCommandInterceptors) { + this.customPostCommandInterceptors = customPostCommandInterceptors; + return this; + } + + public List getCommandInterceptors() { + return commandInterceptors; + } + + public AbstractEngineConfiguration setCommandInterceptors(List commandInterceptors) { + this.commandInterceptors = commandInterceptors; + return this; + } + + public Map getEngineConfigurations() { + return engineConfigurations; + } + + public AbstractEngineConfiguration setEngineConfigurations(Map engineConfigurations) { + this.engineConfigurations = engineConfigurations; + return this; + } + + public void addEngineConfiguration(String key, String scopeType, AbstractEngineConfiguration engineConfiguration) { + if (engineConfigurations == null) { + engineConfigurations = new HashMap<>(); + } + engineConfigurations.put(key, engineConfiguration); + engineConfigurations.put(scopeType, engineConfiguration); + } + + public Map getServiceConfigurations() { + return serviceConfigurations; + } + + public AbstractEngineConfiguration setServiceConfigurations(Map serviceConfigurations) { + this.serviceConfigurations = serviceConfigurations; + return this; + } + + public void addServiceConfiguration(String key, AbstractServiceConfiguration serviceConfiguration) { + if (serviceConfigurations == null) { + serviceConfigurations = new HashMap<>(); + } + serviceConfigurations.put(key, serviceConfiguration); + } + + public Map getEventRegistryEventConsumers() { + return eventRegistryEventConsumers; + } + + public AbstractEngineConfiguration setEventRegistryEventConsumers(Map eventRegistryEventConsumers) { + this.eventRegistryEventConsumers = eventRegistryEventConsumers; + return this; + } + + public void addEventRegistryEventConsumer(String key, EventRegistryEventConsumer eventRegistryEventConsumer) { + if (eventRegistryEventConsumers == null) { + eventRegistryEventConsumers = new HashMap<>(); + } + eventRegistryEventConsumers.put(key, eventRegistryEventConsumer); + } + + public AbstractEngineConfiguration setDefaultCommandInterceptors(Collection defaultCommandInterceptors) { + this.defaultCommandInterceptors = defaultCommandInterceptors; + return this; + } + + public SqlSessionFactory getSqlSessionFactory() { + return sqlSessionFactory; + } + + public AbstractEngineConfiguration setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { + this.sqlSessionFactory = sqlSessionFactory; + return this; + } + + public boolean isDbHistoryUsed() { + return isDbHistoryUsed; + } + + public AbstractEngineConfiguration setDbHistoryUsed(boolean isDbHistoryUsed) { + this.isDbHistoryUsed = isDbHistoryUsed; + return this; + } + + public DbSqlSessionFactory getDbSqlSessionFactory() { + return dbSqlSessionFactory; + } + + public AbstractEngineConfiguration setDbSqlSessionFactory(DbSqlSessionFactory dbSqlSessionFactory) { + this.dbSqlSessionFactory = dbSqlSessionFactory; + return this; + } + + public TransactionFactory getTransactionFactory() { + return transactionFactory; + } + + public AbstractEngineConfiguration setTransactionFactory(TransactionFactory transactionFactory) { + this.transactionFactory = transactionFactory; + return this; + } + + public TransactionContextFactory getTransactionContextFactory() { + return transactionContextFactory; + } + + public AbstractEngineConfiguration setTransactionContextFactory(TransactionContextFactory transactionContextFactory) { + this.transactionContextFactory = transactionContextFactory; + return this; + } + + public int getMaxNrOfStatementsInBulkInsert() { + return maxNrOfStatementsInBulkInsert; + } + + public AbstractEngineConfiguration setMaxNrOfStatementsInBulkInsert(int maxNrOfStatementsInBulkInsert) { + this.maxNrOfStatementsInBulkInsert = maxNrOfStatementsInBulkInsert; + return this; + } + + public boolean isBulkInsertEnabled() { + return isBulkInsertEnabled; + } + + public AbstractEngineConfiguration setBulkInsertEnabled(boolean isBulkInsertEnabled) { + this.isBulkInsertEnabled = isBulkInsertEnabled; + return this; + } + + public Set> getCustomMybatisMappers() { + return customMybatisMappers; + } + + public AbstractEngineConfiguration setCustomMybatisMappers(Set> customMybatisMappers) { + this.customMybatisMappers = customMybatisMappers; + return this; + } + + public Set getCustomMybatisXMLMappers() { + return customMybatisXMLMappers; + } + + public AbstractEngineConfiguration setCustomMybatisXMLMappers(Set customMybatisXMLMappers) { + this.customMybatisXMLMappers = customMybatisXMLMappers; + return this; + } + + public Set getDependentEngineMyBatisXmlMappers() { + return dependentEngineMyBatisXmlMappers; + } + + public AbstractEngineConfiguration setCustomMybatisInterceptors(List customMybatisInterceptors) { + this.customMybatisInterceptors = customMybatisInterceptors; + return this; + } + + public List getCustomMybatisInterceptors() { + return customMybatisInterceptors; + } + + public AbstractEngineConfiguration setDependentEngineMyBatisXmlMappers(Set dependentEngineMyBatisXmlMappers) { + this.dependentEngineMyBatisXmlMappers = dependentEngineMyBatisXmlMappers; + return this; + } + + public List getDependentEngineMybatisTypeAliasConfigs() { + return dependentEngineMybatisTypeAliasConfigs; + } + + public AbstractEngineConfiguration setDependentEngineMybatisTypeAliasConfigs(List dependentEngineMybatisTypeAliasConfigs) { + this.dependentEngineMybatisTypeAliasConfigs = dependentEngineMybatisTypeAliasConfigs; + return this; + } + + public List getDependentEngineMybatisTypeHandlerConfigs() { + return dependentEngineMybatisTypeHandlerConfigs; + } + + public AbstractEngineConfiguration setDependentEngineMybatisTypeHandlerConfigs(List dependentEngineMybatisTypeHandlerConfigs) { + this.dependentEngineMybatisTypeHandlerConfigs = dependentEngineMybatisTypeHandlerConfigs; + return this; + } + + public List getCustomSessionFactories() { + return customSessionFactories; + } + + public AbstractEngineConfiguration addCustomSessionFactory(SessionFactory sessionFactory) { + if (customSessionFactories == null) { + customSessionFactories = new ArrayList<>(); + } + customSessionFactories.add(sessionFactory); + return this; + } + + public AbstractEngineConfiguration setCustomSessionFactories(List customSessionFactories) { + this.customSessionFactories = customSessionFactories; + return this; + } + + public boolean isUsingRelationalDatabase() { + return usingRelationalDatabase; + } + + public AbstractEngineConfiguration setUsingRelationalDatabase(boolean usingRelationalDatabase) { + this.usingRelationalDatabase = usingRelationalDatabase; + return this; + } + + public boolean isUsingSchemaMgmt() { + return usingSchemaMgmt; + } + + public AbstractEngineConfiguration setUsingSchemaMgmt(boolean usingSchema) { + this.usingSchemaMgmt = usingSchema; + return this; + } + + public String getDatabaseTablePrefix() { + return databaseTablePrefix; + } + + public AbstractEngineConfiguration setDatabaseTablePrefix(String databaseTablePrefix) { + this.databaseTablePrefix = databaseTablePrefix; + return this; + } + + public String getDatabaseWildcardEscapeCharacter() { + return databaseWildcardEscapeCharacter; + } + + public AbstractEngineConfiguration setDatabaseWildcardEscapeCharacter(String databaseWildcardEscapeCharacter) { + this.databaseWildcardEscapeCharacter = databaseWildcardEscapeCharacter; + return this; + } + + public String getDatabaseCatalog() { + return databaseCatalog; + } + + public AbstractEngineConfiguration setDatabaseCatalog(String databaseCatalog) { + this.databaseCatalog = databaseCatalog; + return this; + } + + public String getDatabaseSchema() { + return databaseSchema; + } + + public AbstractEngineConfiguration setDatabaseSchema(String databaseSchema) { + this.databaseSchema = databaseSchema; + return this; + } + + public boolean isTablePrefixIsSchema() { + return tablePrefixIsSchema; + } + + public AbstractEngineConfiguration setTablePrefixIsSchema(boolean tablePrefixIsSchema) { + this.tablePrefixIsSchema = tablePrefixIsSchema; + return this; + } + + public boolean isAlwaysLookupLatestDefinitionVersion() { + return alwaysLookupLatestDefinitionVersion; + } + + public AbstractEngineConfiguration setAlwaysLookupLatestDefinitionVersion(boolean alwaysLookupLatestDefinitionVersion) { + this.alwaysLookupLatestDefinitionVersion = alwaysLookupLatestDefinitionVersion; + return this; + } + + public boolean isFallbackToDefaultTenant() { + return fallbackToDefaultTenant; + } + + public AbstractEngineConfiguration setFallbackToDefaultTenant(boolean fallbackToDefaultTenant) { + this.fallbackToDefaultTenant = fallbackToDefaultTenant; + return this; + } + + /** + * @return name of the default tenant + * @deprecated use {@link AbstractEngineConfiguration#getDefaultTenantProvider()} instead + */ + @Deprecated + public String getDefaultTenantValue() { + return getDefaultTenantProvider().getDefaultTenant(null, null, null); + } + + public AbstractEngineConfiguration setDefaultTenantValue(String defaultTenantValue) { + this.defaultTenantProvider = (tenantId, scope, scopeKey) -> defaultTenantValue; + return this; + } + + public DefaultTenantProvider getDefaultTenantProvider() { + return defaultTenantProvider; + } + + public AbstractEngineConfiguration setDefaultTenantProvider(DefaultTenantProvider defaultTenantProvider) { + this.defaultTenantProvider = defaultTenantProvider; + return this; + } + + public boolean isEnableLogSqlExecutionTime() { + return enableLogSqlExecutionTime; + } + + public void setEnableLogSqlExecutionTime(boolean enableLogSqlExecutionTime) { + this.enableLogSqlExecutionTime = enableLogSqlExecutionTime; + } + + public Map, SessionFactory> getSessionFactories() { + return sessionFactories; + } + + public AbstractEngineConfiguration setSessionFactories(Map, SessionFactory> sessionFactories) { + this.sessionFactories = sessionFactories; + return this; + } + + public String getDatabaseSchemaUpdate() { + return databaseSchemaUpdate; + } + + public AbstractEngineConfiguration setDatabaseSchemaUpdate(String databaseSchemaUpdate) { + this.databaseSchemaUpdate = databaseSchemaUpdate; + return this; + } + + public boolean isUseLockForDatabaseSchemaUpdate() { + return useLockForDatabaseSchemaUpdate; + } + + public AbstractEngineConfiguration setUseLockForDatabaseSchemaUpdate(boolean useLockForDatabaseSchemaUpdate) { + this.useLockForDatabaseSchemaUpdate = useLockForDatabaseSchemaUpdate; + return this; + } + + public boolean isEnableEventDispatcher() { + return enableEventDispatcher; + } + + public AbstractEngineConfiguration setEnableEventDispatcher(boolean enableEventDispatcher) { + this.enableEventDispatcher = enableEventDispatcher; + return this; + } + + public FlowableEventDispatcher getEventDispatcher() { + return eventDispatcher; + } + + public AbstractEngineConfiguration setEventDispatcher(FlowableEventDispatcher eventDispatcher) { + this.eventDispatcher = eventDispatcher; + return this; + } + + public List getEventListeners() { + return eventListeners; + } + + public AbstractEngineConfiguration setEventListeners(List eventListeners) { + this.eventListeners = eventListeners; + return this; + } + + public Map> getTypedEventListeners() { + return typedEventListeners; + } + + public AbstractEngineConfiguration setTypedEventListeners(Map> typedEventListeners) { + this.typedEventListeners = typedEventListeners; + return this; + } + + public List getAdditionalEventDispatchActions() { + return additionalEventDispatchActions; + } + + public AbstractEngineConfiguration setAdditionalEventDispatchActions(List additionalEventDispatchActions) { + this.additionalEventDispatchActions = additionalEventDispatchActions; + return this; + } + + public void initEventDispatcher() { + if (this.eventDispatcher == null) { + this.eventDispatcher = new FlowableEventDispatcherImpl(); + } + + initAdditionalEventDispatchActions(); + + this.eventDispatcher.setEnabled(enableEventDispatcher); + + initEventListeners(); + initTypedEventListeners(); + } + + protected void initEventListeners() { + if (eventListeners != null) { + for (FlowableEventListener listenerToAdd : eventListeners) { + this.eventDispatcher.addEventListener(listenerToAdd); + } + } + } + + protected void initAdditionalEventDispatchActions() { + if (this.additionalEventDispatchActions == null) { + this.additionalEventDispatchActions = new ArrayList<>(); + } + } + + protected void initTypedEventListeners() { + if (typedEventListeners != null) { + for (Map.Entry> listenersToAdd : typedEventListeners.entrySet()) { + // Extract types from the given string + FlowableEngineEventType[] types = FlowableEngineEventType.getTypesFromString(listenersToAdd.getKey()); + + for (FlowableEventListener listenerToAdd : listenersToAdd.getValue()) { + this.eventDispatcher.addEventListener(listenerToAdd, types); + } + } + } + } + + public boolean isLoggingSessionEnabled() { + return loggingListener != null; + } + + public LoggingListener getLoggingListener() { + return loggingListener; + } + + public void setLoggingListener(LoggingListener loggingListener) { + this.loggingListener = loggingListener; + } + + public Clock getClock() { + return clock; + } + + public AbstractEngineConfiguration setClock(Clock clock) { + this.clock = clock; + return this; + } + + public ObjectMapper getObjectMapper() { + return objectMapper; + } + + public AbstractEngineConfiguration setObjectMapper(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + return this; + } + + public int getMaxLengthString() { + if (maxLengthStringVariableType == -1) { + if ("oracle".equalsIgnoreCase(databaseType)) { + return DEFAULT_ORACLE_MAX_LENGTH_STRING; + } else { + return DEFAULT_GENERIC_MAX_LENGTH_STRING; + } + } else { + return maxLengthStringVariableType; + } + } + + public int getMaxLengthStringVariableType() { + return maxLengthStringVariableType; + } + + public AbstractEngineConfiguration setMaxLengthStringVariableType(int maxLengthStringVariableType) { + this.maxLengthStringVariableType = maxLengthStringVariableType; + return this; + } + + public PropertyDataManager getPropertyDataManager() { + return propertyDataManager; + } + + public Duration getLockPollRate() { + return lockPollRate; + } + + public AbstractEngineConfiguration setLockPollRate(Duration lockPollRate) { + this.lockPollRate = lockPollRate; + return this; + } + + public Duration getSchemaLockWaitTime() { + return schemaLockWaitTime; + } + + public void setSchemaLockWaitTime(Duration schemaLockWaitTime) { + this.schemaLockWaitTime = schemaLockWaitTime; + } + + public AbstractEngineConfiguration setPropertyDataManager(PropertyDataManager propertyDataManager) { + this.propertyDataManager = propertyDataManager; + return this; + } + + public PropertyEntityManager getPropertyEntityManager() { + return propertyEntityManager; + } + + public AbstractEngineConfiguration setPropertyEntityManager(PropertyEntityManager propertyEntityManager) { + this.propertyEntityManager = propertyEntityManager; + return this; + } + + public ByteArrayDataManager getByteArrayDataManager() { + return byteArrayDataManager; + } + + public AbstractEngineConfiguration setByteArrayDataManager(ByteArrayDataManager byteArrayDataManager) { + this.byteArrayDataManager = byteArrayDataManager; + return this; + } + + public ByteArrayEntityManager getByteArrayEntityManager() { + return byteArrayEntityManager; + } + + public AbstractEngineConfiguration setByteArrayEntityManager(ByteArrayEntityManager byteArrayEntityManager) { + this.byteArrayEntityManager = byteArrayEntityManager; + return this; + } + + public TableDataManager getTableDataManager() { + return tableDataManager; + } + + public AbstractEngineConfiguration setTableDataManager(TableDataManager tableDataManager) { + this.tableDataManager = tableDataManager; + return this; + } + + public List getDeployers() { + return deployers; + } + + public AbstractEngineConfiguration setDeployers(List deployers) { + this.deployers = deployers; + return this; + } + + public List getCustomPreDeployers() { + return customPreDeployers; + } + + public AbstractEngineConfiguration setCustomPreDeployers(List customPreDeployers) { + this.customPreDeployers = customPreDeployers; + return this; + } + + public List getCustomPostDeployers() { + return customPostDeployers; + } + + public AbstractEngineConfiguration setCustomPostDeployers(List customPostDeployers) { + this.customPostDeployers = customPostDeployers; + return this; + } + + public boolean isEnableConfiguratorServiceLoader() { + return enableConfiguratorServiceLoader; + } + + public AbstractEngineConfiguration setEnableConfiguratorServiceLoader(boolean enableConfiguratorServiceLoader) { + this.enableConfiguratorServiceLoader = enableConfiguratorServiceLoader; + return this; + } + + public List getConfigurators() { + return configurators; + } + + public AbstractEngineConfiguration addConfigurator(EngineConfigurator configurator) { + if (configurators == null) { + configurators = new ArrayList<>(); + } + configurators.add(configurator); + return this; + } + + /** + * @return All {@link EngineConfigurator} instances. Will only contain values after init of the engine. + * Use the {@link #getConfigurators()} or {@link #addConfigurator(EngineConfigurator)} methods otherwise. + */ + public List getAllConfigurators() { + return allConfigurators; + } + + public AbstractEngineConfiguration setConfigurators(List configurators) { + this.configurators = configurators; + return this; + } + + public EngineConfigurator getIdmEngineConfigurator() { + return idmEngineConfigurator; + } + + public AbstractEngineConfiguration setIdmEngineConfigurator(EngineConfigurator idmEngineConfigurator) { + this.idmEngineConfigurator = idmEngineConfigurator; + return this; + } + + public EngineConfigurator getEventRegistryConfigurator() { + return eventRegistryConfigurator; + } + + public AbstractEngineConfiguration setEventRegistryConfigurator(EngineConfigurator eventRegistryConfigurator) { + this.eventRegistryConfigurator = eventRegistryConfigurator; + return this; + } + + public AbstractEngineConfiguration setForceCloseMybatisConnectionPool(boolean forceCloseMybatisConnectionPool) { + this.forceCloseMybatisConnectionPool = forceCloseMybatisConnectionPool; + return this; + } + + public boolean isForceCloseMybatisConnectionPool() { + return forceCloseMybatisConnectionPool; + } +} diff --git a/sql/db2/flowable-patch/src/main/resources/META-INF/package-info.md b/sql/db2/flowable-patch/src/main/resources/META-INF/package-info.md new file mode 100644 index 000000000..1932c7a3f --- /dev/null +++ b/sql/db2/flowable-patch/src/main/resources/META-INF/package-info.md @@ -0,0 +1 @@ +防止IDEA将`.`和`/`混为一谈 \ No newline at end of file diff --git a/sql/db2/flowable-patch/src/main/resources/META-INF/services/liquibase.database.Database b/sql/db2/flowable-patch/src/main/resources/META-INF/services/liquibase.database.Database new file mode 100644 index 000000000..efbcfcca2 --- /dev/null +++ b/sql/db2/flowable-patch/src/main/resources/META-INF/services/liquibase.database.Database @@ -0,0 +1,21 @@ +liquibase.database.core.CockroachDatabase +liquibase.database.core.DB2Database +liquibase.database.core.Db2zDatabase +liquibase.database.core.DerbyDatabase +liquibase.database.core.Firebird3Database +liquibase.database.core.FirebirdDatabase +liquibase.database.core.H2Database +liquibase.database.core.HsqlDatabase +liquibase.database.core.InformixDatabase +liquibase.database.core.Ingres9Database +liquibase.database.core.MSSQLDatabase +liquibase.database.core.MariaDBDatabase +liquibase.database.core.MockDatabase +liquibase.database.core.MySQLDatabase +liquibase.database.core.OracleDatabase +liquibase.database.core.PostgresDatabase +liquibase.database.core.SQLiteDatabase +liquibase.database.core.SybaseASADatabase +liquibase.database.core.SybaseDatabase +liquibase.database.core.DmDatabase +liquibase.database.core.UnsupportedDatabase diff --git a/sql/mysql/ruoyi-vue-pro.sql b/sql/mysql/ruoyi-vue-pro.sql index 9fec3ee01..ebae9f87f 100644 --- a/sql/mysql/ruoyi-vue-pro.sql +++ b/sql/mysql/ruoyi-vue-pro.sql @@ -2292,7 +2292,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2302, '支付通知查询', 'pay:notify:query', 3, 1, 2301, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-07-20 04:41:32', '', '2023-07-20 04:41:32', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2303, '拼团活动', '', 2, 3, 2030, 'combination', 'fa:group', '', '', 0, b'1', b'1', b'1', '1', '2023-08-12 17:19:54', '1', '2023-08-12 17:20:05', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2304, '拼团商品', '', 2, 1, 2303, 'acitivity', 'ep:apple', 'mall/promotion/combination/activity/index', 'PromotionCombinationActivity', 0, b'1', b'1', b'1', '1', '2023-08-12 17:22:03', '1', '2023-08-12 17:22:29', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2305, '拼团活动查询', 'promotion:combination-activity:query ', 3, 1, 2304, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-08-12 17:54:32', '1', '2023-08-12 17:54:32', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2305, '拼团活动查询', 'promotion:combination-activity:query', 3, 1, 2304, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-08-12 17:54:32', '1', '2023-08-12 17:54:32', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2306, '拼团活动创建', 'promotion:combination-activity:create', 3, 2, 2304, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-08-12 17:54:49', '1', '2023-08-12 17:54:49', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2307, '拼团活动更新', 'promotion:combination-activity:update', 3, 3, 2304, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-08-12 17:55:04', '1', '2023-08-12 17:55:04', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2308, '拼团活动删除', 'promotion:combination-activity:delete', 3, 4, 2304, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-08-12 17:55:23', '1', '2023-08-12 17:55:23', b'0'); diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/Telephone.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/Telephone.java new file mode 100644 index 000000000..910601fd0 --- /dev/null +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/Telephone.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.framework.common.validation; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.*; + +@Target({ + ElementType.METHOD, + ElementType.FIELD, + ElementType.ANNOTATION_TYPE, + ElementType.CONSTRUCTOR, + ElementType.PARAMETER, + ElementType.TYPE_USE +}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Constraint( + validatedBy = TelephoneValidator.class +) +public @interface Telephone { + + String message() default "电话格式不正确"; + + Class[] groups() default {}; + + Class[] payload() default {}; + +} diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/TelephoneValidator.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/TelephoneValidator.java new file mode 100644 index 000000000..d214cfeef --- /dev/null +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/TelephoneValidator.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.framework.common.validation; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.core.util.PhoneUtil; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +public class TelephoneValidator implements ConstraintValidator { + + @Override + public void initialize(Telephone annotation) { + } + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + // 如果手机号为空,默认不校验,即校验通过 + if (CharSequenceUtil.isEmpty(value)) { + return true; + } + // 校验手机 + return PhoneUtil.isTel(value) || PhoneUtil.isPhone(value); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test-integration/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsClientIntegrationTest.java b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test-integration/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsClientIntegrationTest.java deleted file mode 100644 index 36f9dec82..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test-integration/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsClientIntegrationTest.java +++ /dev/null @@ -1,55 +0,0 @@ -package cn.iocoder.yudao.framework.sms.core.client.impl.aliyun; - -import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult; -import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO; -import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO; -import cn.iocoder.yudao.framework.sms.core.client.impl.aliyun.AliyunSmsClient; -import cn.iocoder.yudao.framework.sms.core.enums.SmsChannelEnum; -import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; - -/** - * {@link AliyunSmsClient} 的集成测试 - */ -public class AliyunSmsClientIntegrationTest { - - private static AliyunSmsClient smsClient; - - @BeforeAll - public static void before() { - // 创建配置类 - SmsChannelProperties properties = new SmsChannelProperties(); - properties.setId(1L); - properties.setSignature("Ballcat"); - properties.setCode(SmsChannelEnum.ALIYUN.getCode()); - properties.setApiKey(System.getenv("ALIYUN_ACCESS_KEY")); - properties.setApiSecret(System.getenv("ALIYUN_SECRET_KEY")); - // 创建客户端 - smsClient = new AliyunSmsClient(properties); - smsClient.init(); - } - - @Test - public void testSendSms() { - List> templateParams = new ArrayList<>(); - templateParams.add(new KeyValue<>("code", "1024")); -// templateParams.put("operation", "嘿嘿"); -// SmsResult result = smsClient.send(1L, "15601691399", "4372216", templateParams); - SmsCommonResult result = smsClient.sendSms(1L, "15601691399", - "SMS_207945135", templateParams); - System.out.println(result); - } - - @Test - public void testGetSmsTemplate() { - String apiTemplateId = "SMS_2079451351"; - SmsCommonResult result = smsClient.getSmsTemplate(apiTemplateId); - System.out.println(result); - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test-integration/java/cn/iocoder/yudao/framework/sms/core/client/impl/debug/DebugDingTalkSmsClientIntegrationTest.java b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test-integration/java/cn/iocoder/yudao/framework/sms/core/client/impl/debug/DebugDingTalkSmsClientIntegrationTest.java deleted file mode 100644 index 47de977cb..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test-integration/java/cn/iocoder/yudao/framework/sms/core/client/impl/debug/DebugDingTalkSmsClientIntegrationTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package cn.iocoder.yudao.framework.sms.core.client.impl.debug; - -import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult; -import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO; -import cn.iocoder.yudao.framework.sms.core.client.impl.debug.DebugDingTalkSmsClient; -import cn.iocoder.yudao.framework.sms.core.enums.SmsChannelEnum; -import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; - -/** - * {@link DebugDingTalkSmsClient} 的集成测试 - */ -public class DebugDingTalkSmsClientIntegrationTest { - - private static DebugDingTalkSmsClient smsClient; - - @BeforeAll - public static void init() { - // 创建配置类 - SmsChannelProperties properties = new SmsChannelProperties(); - properties.setId(1L); - properties.setSignature("芋道"); - properties.setCode(SmsChannelEnum.DEBUG_DING_TALK.getCode()); - properties.setApiKey("696b5d8ead48071237e4aa5861ff08dbadb2b4ded1c688a7b7c9afc615579859"); - properties.setApiSecret("SEC5c4e5ff888bc8a9923ae47f59e7ccd30af1f14d93c55b4e2c9cb094e35aeed67"); - // 创建客户端 - smsClient = new DebugDingTalkSmsClient(properties); - smsClient.init(); - } - - @Test - public void testSendSms() { - List> templateParams = new ArrayList<>(); - templateParams.add(new KeyValue<>("code", "1024")); - templateParams.add(new KeyValue<>("operation", "嘿嘿")); -// SmsResult result = smsClient.send(1L, "15601691399", "4372216", templateParams); - SmsCommonResult result = smsClient.sendSms(1L, "15601691399", "4383920", templateParams); - System.out.println(result); - } - -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java index 92c7c154c..4e4e68d6f 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java @@ -1,12 +1,12 @@ package cn.iocoder.yudao.module.bpm.dal.dataobject.oa; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; import java.time.LocalDateTime; -import java.util.*; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; /** * OA 请假申请 DO @@ -39,7 +39,6 @@ public class BpmOALeaveDO extends BaseDO { /** * 请假类型 */ - @TableField("`type`") private String type; /** * 原因