田源
2025-04-03 9b4433fddf5b401edb0aace8a404ac733b122702
Source/BladeX-Tool/blade-starter-flowable/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java
对比新文件
@@ -0,0 +1,1647 @@
/* 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 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.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher;
import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
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.*;
import org.flowable.common.engine.impl.event.EventDispatchAction;
import org.flowable.common.engine.impl.interceptor.*;
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.Entity;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.sql.*;
import java.util.*;
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;
   protected int jdbcMaxIdleConnections;
   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<Void> schemaManagementCmd;
   protected String databaseSchemaUpdate = DB_SCHEMA_UPDATE_FALSE;
   protected String xmlEncoding = "UTF-8";
   // COMMAND EXECUTORS ///////////////////////////////////////////////
   protected CommandExecutor commandExecutor;
   protected Collection<? extends CommandInterceptor> defaultCommandInterceptors;
   protected CommandConfig defaultCommandConfig;
   protected CommandConfig schemaCommandConfig;
   protected CommandContextFactory commandContextFactory;
   protected CommandInterceptor commandInvoker;
   protected List<CommandInterceptor> customPreCommandInterceptors;
   protected List<CommandInterceptor> customPostCommandInterceptors;
   protected List<CommandInterceptor> commandInterceptors;
   protected Map<String, AbstractEngineConfiguration> engineConfigurations = new HashMap<>();
   protected Map<String, AbstractServiceConfiguration> 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;
   // 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.
    * <p>
    * By default: 100 (75 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 = 60; // currently Execution has most params (31). 2000 / 31 = 64.
   protected Set<Class<?>> customMybatisMappers;
   protected Set<String> customMybatisXMLMappers;
   protected List<Interceptor> customMybatisInterceptors;
   protected Set<String> dependentEngineMyBatisXmlMappers;
   protected List<MybatisTypeAliasConfigurator> dependentEngineMybatisTypeAliasConfigs;
   protected List<MybatisTypeHandlerConfigurator> dependentEngineMybatisTypeHandlerConfigs;
   // SESSION FACTORIES ///////////////////////////////////////////////
   protected List<SessionFactory> customSessionFactories;
   protected Map<Class<?>, SessionFactory> sessionFactories;
   protected boolean enableEventDispatcher = true;
   protected FlowableEventDispatcher eventDispatcher;
   protected List<FlowableEventListener> eventListeners;
   protected Map<String, List<FlowableEventListener>> typedEventListeners;
   protected List<EventDispatchAction> additionalEventDispatchActions;
   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 usefil for custom implementations that do not use relational databases at all.
    * Setting {@link #usingRelationalDatabase} to true will automotically 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_'.
    *
    * <p />
    * <strong>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.</strong>
    */
   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();
   protected List<EngineDeployer> customPreDeployers;
   protected List<EngineDeployer> customPostDeployers;
   protected List<EngineDeployer> deployers;
   // CONFIGURATORS ////////////////////////////////////////////////////////////
   protected boolean enableConfiguratorServiceLoader = true; // Enabled by default. In certain environments this should be set to false (eg osgi)
   protected List<EngineConfigurator> configurators; // The injected configurators
   protected List<EngineConfigurator> allConfigurators; // Including auto-discovered configurators
   protected EngineConfigurator idmEngineConfigurator;
   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 final String DATABASE_TYPE_DM = "oracle";
   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/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("DM DBMS", DATABASE_TYPE_DM);
      databaseTypeMappings.setProperty(PRODUCT_NAME_CRDB, DATABASE_TYPE_COCKROACHDB);
      return databaseTypeMappings;
   }
   protected Map<Object, Object> beans;
   protected IdGenerator idGenerator;
   protected boolean usePrefixId;
   protected Clock clock;
   // 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() {
      engineConfigurations.put(getEngineCfgKey(), 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)) {
            PreparedStatement preparedStatement = connection.prepareStatement("select version() as version;");
            ResultSet resultSet = preparedStatement.executeQuery();
            String version = null;
            if (resultSet.next()) {
               version = resultSet.getString("version");
            }
            resultSet.close();
            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) {
         logger.error("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<? extends CommandInterceptor> getDefaultCommandInterceptors() {
      if (defaultCommandInterceptors == null) {
         List<CommandInterceptor> 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);
            engineConfigurations.put(engineCfgKey, this);
            commandContextInterceptor.setEngineConfigurations(engineConfigurations);
            commandContextInterceptor.setServiceConfigurations(serviceConfigurations);
            commandContextInterceptor.setCurrentEngineConfigurationKey(engineCfgKey);
            interceptors.add(commandContextInterceptor);
         }
         if (transactionContextFactory != null) {
            interceptors.add(new TransactionContextInterceptor(transactionContextFactory));
         }
         List<CommandInterceptor> additionalCommandInterceptors = getAdditionalDefaultCommandInterceptors();
         if (additionalCommandInterceptors != null) {
            interceptors.addAll(additionalCommandInterceptors);
         }
         defaultCommandInterceptors = interceptors;
      }
      return defaultCommandInterceptors;
   }
   public abstract String getEngineCfgKey();
   public List<CommandInterceptor> getAdditionalDefaultCommandInterceptors() {
      return null;
   }
   public void initCommandExecutor() {
      if (commandExecutor == null) {
         CommandInterceptor first = initInterceptorChain(commandInterceptors);
         commandExecutor = new CommandExecutorImpl(getDefaultCommandConfig(), first);
      }
   }
   public CommandInterceptor initInterceptorChain(List<CommandInterceptor> 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 initClock() {
      if (clock == null) {
         clock = new DefaultClockImpl();
      }
   }
   // 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));
         commandContextFactory.setSessionFactories(sessionFactories);
      }
      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<Class<? extends Entity>> insertOrder, List<Class<? extends Entity>> deleteOrder) {
      if (insertOrder != null) {
         for (Class<? extends Entity> clazz : insertOrder) {
            dbSqlSessionFactory.getInsertionOrder().add(clazz);
            if (isBulkInsertEnabled) {
               dbSqlSessionFactory.getBulkInserteableEntityClasses().add(clazz);
            }
         }
      }
      if (deleteOrder != null) {
         for (Class<? extends Entity> 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("limitOuterJoinBetween", "");
            properties.put("limitBeforeNativeQuery", "");
            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);
         }
      }
   }
   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);
      initCustomMybatisMappers(configuration);
      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()) {
            configuration.addMapper(clazz);
         }
      }
   }
   public void initMybatisTypeHandlers(Configuration configuration) {
      // To be extended
   }
   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();
      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);
      return 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 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<EngineConfigurator> 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)
            Collections.sort(allConfigurators, new Comparator<EngineConfigurator>() {
               @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<EngineConfigurator> 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);
      }
   }
   // 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 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<Void> getSchemaManagementCmd() {
      return schemaManagementCmd;
   }
   public AbstractEngineConfiguration setSchemaManagementCmd(Command<Void> 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<Object, Object> getBeans() {
      return beans;
   }
   public AbstractEngineConfiguration setBeans(Map<Object, Object> 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 List<CommandInterceptor> getCustomPreCommandInterceptors() {
      return customPreCommandInterceptors;
   }
   public AbstractEngineConfiguration setCustomPreCommandInterceptors(List<CommandInterceptor> customPreCommandInterceptors) {
      this.customPreCommandInterceptors = customPreCommandInterceptors;
      return this;
   }
   public List<CommandInterceptor> getCustomPostCommandInterceptors() {
      return customPostCommandInterceptors;
   }
   public AbstractEngineConfiguration setCustomPostCommandInterceptors(List<CommandInterceptor> customPostCommandInterceptors) {
      this.customPostCommandInterceptors = customPostCommandInterceptors;
      return this;
   }
   public List<CommandInterceptor> getCommandInterceptors() {
      return commandInterceptors;
   }
   public AbstractEngineConfiguration setCommandInterceptors(List<CommandInterceptor> commandInterceptors) {
      this.commandInterceptors = commandInterceptors;
      return this;
   }
   public Map<String, AbstractEngineConfiguration> getEngineConfigurations() {
      return engineConfigurations;
   }
   public AbstractEngineConfiguration setEngineConfigurations(Map<String, AbstractEngineConfiguration> engineConfigurations) {
      this.engineConfigurations = engineConfigurations;
      return this;
   }
   public void addEngineConfiguration(String key, AbstractEngineConfiguration engineConfiguration) {
      if (engineConfigurations == null) {
         engineConfigurations = new HashMap<>();
      }
      engineConfigurations.put(key, engineConfiguration);
   }
   public Map<String, AbstractServiceConfiguration> getServiceConfigurations() {
      return serviceConfigurations;
   }
   public AbstractEngineConfiguration setServiceConfigurations(Map<String, AbstractServiceConfiguration> serviceConfigurations) {
      this.serviceConfigurations = serviceConfigurations;
      return this;
   }
   public void addServiceConfiguration(String key, AbstractServiceConfiguration serviceConfiguration) {
      if (serviceConfigurations == null) {
         serviceConfigurations = new HashMap<>();
      }
      serviceConfigurations.put(key, serviceConfiguration);
   }
   public void setDefaultCommandInterceptors(Collection<? extends CommandInterceptor> defaultCommandInterceptors) {
      this.defaultCommandInterceptors = defaultCommandInterceptors;
   }
   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<Class<?>> getCustomMybatisMappers() {
      return customMybatisMappers;
   }
   public AbstractEngineConfiguration setCustomMybatisMappers(Set<Class<?>> customMybatisMappers) {
      this.customMybatisMappers = customMybatisMappers;
      return this;
   }
   public Set<String> getCustomMybatisXMLMappers() {
      return customMybatisXMLMappers;
   }
   public AbstractEngineConfiguration setCustomMybatisXMLMappers(Set<String> customMybatisXMLMappers) {
      this.customMybatisXMLMappers = customMybatisXMLMappers;
      return this;
   }
   public Set<String> getDependentEngineMyBatisXmlMappers() {
      return dependentEngineMyBatisXmlMappers;
   }
   public AbstractEngineConfiguration setCustomMybatisInterceptors(List<Interceptor> customMybatisInterceptors) {
      this.customMybatisInterceptors = customMybatisInterceptors;
      return  this;
   }
   public List<Interceptor> getCustomMybatisInterceptors() {
      return customMybatisInterceptors;
   }
   public AbstractEngineConfiguration setDependentEngineMyBatisXmlMappers(Set<String> dependentEngineMyBatisXmlMappers) {
      this.dependentEngineMyBatisXmlMappers = dependentEngineMyBatisXmlMappers;
      return this;
   }
   public List<MybatisTypeAliasConfigurator> getDependentEngineMybatisTypeAliasConfigs() {
      return dependentEngineMybatisTypeAliasConfigs;
   }
   public AbstractEngineConfiguration setDependentEngineMybatisTypeAliasConfigs(List<MybatisTypeAliasConfigurator> dependentEngineMybatisTypeAliasConfigs) {
      this.dependentEngineMybatisTypeAliasConfigs = dependentEngineMybatisTypeAliasConfigs;
      return this;
   }
   public List<MybatisTypeHandlerConfigurator> getDependentEngineMybatisTypeHandlerConfigs() {
      return dependentEngineMybatisTypeHandlerConfigs;
   }
   public AbstractEngineConfiguration setDependentEngineMybatisTypeHandlerConfigs(List<MybatisTypeHandlerConfigurator> dependentEngineMybatisTypeHandlerConfigs) {
      this.dependentEngineMybatisTypeHandlerConfigs = dependentEngineMybatisTypeHandlerConfigs;
      return this;
   }
   public List<SessionFactory> getCustomSessionFactories() {
      return customSessionFactories;
   }
   public AbstractEngineConfiguration addCustomSessionFactory(SessionFactory sessionFactory) {
      if (customSessionFactories == null) {
         customSessionFactories = new ArrayList<>();
      }
      customSessionFactories.add(sessionFactory);
      return this;
   }
   public AbstractEngineConfiguration setCustomSessionFactories(List<SessionFactory> 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<Class<?>, SessionFactory> getSessionFactories() {
      return sessionFactories;
   }
   public AbstractEngineConfiguration setSessionFactories(Map<Class<?>, SessionFactory> sessionFactories) {
      this.sessionFactories = sessionFactories;
      return this;
   }
   public String getDatabaseSchemaUpdate() {
      return databaseSchemaUpdate;
   }
   public AbstractEngineConfiguration setDatabaseSchemaUpdate(String databaseSchemaUpdate) {
      this.databaseSchemaUpdate = databaseSchemaUpdate;
      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<FlowableEventListener> getEventListeners() {
      return eventListeners;
   }
   public AbstractEngineConfiguration setEventListeners(List<FlowableEventListener> eventListeners) {
      this.eventListeners = eventListeners;
      return this;
   }
   public Map<String, List<FlowableEventListener>> getTypedEventListeners() {
      return typedEventListeners;
   }
   public AbstractEngineConfiguration setTypedEventListeners(Map<String, List<FlowableEventListener>> typedEventListeners) {
      this.typedEventListeners = typedEventListeners;
      return this;
   }
   public List<EventDispatchAction> getAdditionalEventDispatchActions() {
      return additionalEventDispatchActions;
   }
   public AbstractEngineConfiguration setAdditionalEventDispatchActions(List<EventDispatchAction> additionalEventDispatchActions) {
      this.additionalEventDispatchActions = additionalEventDispatchActions;
      return this;
   }
   public Clock getClock() {
      return clock;
   }
   public AbstractEngineConfiguration setClock(Clock clock) {
      this.clock = clock;
      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 List<EngineDeployer> getDeployers() {
      return deployers;
   }
   public AbstractEngineConfiguration setDeployers(List<EngineDeployer> deployers) {
      this.deployers = deployers;
      return this;
   }
   public List<EngineDeployer> getCustomPreDeployers() {
      return customPreDeployers;
   }
   public AbstractEngineConfiguration setCustomPreDeployers(List<EngineDeployer> customPreDeployers) {
      this.customPreDeployers = customPreDeployers;
      return this;
   }
   public List<EngineDeployer> getCustomPostDeployers() {
      return customPostDeployers;
   }
   public AbstractEngineConfiguration setCustomPostDeployers(List<EngineDeployer> customPostDeployers) {
      this.customPostDeployers = customPostDeployers;
      return this;
   }
   public boolean isEnableConfiguratorServiceLoader() {
      return enableConfiguratorServiceLoader;
   }
   public AbstractEngineConfiguration setEnableConfiguratorServiceLoader(boolean enableConfiguratorServiceLoader) {
      this.enableConfiguratorServiceLoader = enableConfiguratorServiceLoader;
      return this;
   }
   public List<EngineConfigurator> getConfigurators() {
      return configurators;
   }
   public AbstractEngineConfiguration addConfigurator(EngineConfigurator configurator) {
      if (configurators == null) {
         configurators = new ArrayList<>();
      }
      configurators.add(configurator);
      return this;
   }
   public AbstractEngineConfiguration setConfigurators(List<EngineConfigurator> configurators) {
      this.configurators = configurators;
      return this;
   }
   public EngineConfigurator getIdmEngineConfigurator() {
      return idmEngineConfigurator;
   }
   public AbstractEngineConfiguration setIdmEngineConfigurator(EngineConfigurator idmEngineConfigurator) {
      this.idmEngineConfigurator = idmEngineConfigurator;
      return this;
   }
   public AbstractEngineConfiguration setForceCloseMybatisConnectionPool(boolean forceCloseMybatisConnectionPool) {
      this.forceCloseMybatisConnectionPool = forceCloseMybatisConnectionPool;
      return this;
   }
   public boolean isForceCloseMybatisConnectionPool() {
      return forceCloseMybatisConnectionPool;
   }
}