package io.ebeaninternal.server.core; import io.ebean.ExpressionFactory; import io.ebean.cache.ServerCacheManager; import io.ebean.config.ExternalTransactionManager; import io.ebean.Platform; import io.ebean.config.ServerConfig; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebean.config.dbplatform.DbHistorySupport; import io.ebean.event.changelog.ChangeLogListener; import io.ebean.event.changelog.ChangeLogPrepare; import io.ebean.event.changelog.ChangeLogRegister; import io.ebean.event.readaudit.ReadAuditLogger; import io.ebean.event.readaudit.ReadAuditPrepare; import io.ebean.plugin.Plugin; import io.ebean.plugin.SpiServer; import io.ebean.text.json.JsonContext; import io.ebeaninternal.api.SpiBackgroundExecutor; import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.server.autotune.AutoTuneService; import io.ebeaninternal.server.autotune.service.AutoTuneServiceFactory; import io.ebeaninternal.server.cache.DefaultCacheAdapter; import io.ebeaninternal.server.cache.SpiCacheManager; import io.ebeaninternal.server.changelog.DefaultChangeLogListener; import io.ebeaninternal.server.changelog.DefaultChangeLogPrepare; import io.ebeaninternal.server.changelog.DefaultChangeLogRegister; import io.ebeaninternal.server.cluster.ClusterManager; import io.ebeaninternal.server.core.bootup.BootupClasses; import io.ebeaninternal.server.core.timezone.CloneDataTimeZone; import io.ebeaninternal.server.core.timezone.DataTimeZone; import io.ebeaninternal.server.core.timezone.NoDataTimeZone; import io.ebeaninternal.server.core.timezone.SimpleDataTimeZone; import io.ebeaninternal.server.deploy.BeanDescriptorManager; import io.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory; import io.ebeaninternal.server.deploy.parse.DeployCreateProperties; import io.ebeaninternal.server.deploy.parse.DeployInherit; import io.ebeaninternal.server.deploy.parse.DeployUtil; import io.ebeaninternal.server.expression.DefaultExpressionFactory; import io.ebeaninternal.server.persist.Binder; import io.ebeaninternal.server.persist.DefaultPersister; import io.ebeaninternal.server.query.CQueryEngine; import io.ebeaninternal.server.query.DefaultOrmQueryEngine; import io.ebeaninternal.server.query.DefaultRelationalQueryEngine; import io.ebeaninternal.server.readaudit.DefaultReadAuditLogger; import io.ebeaninternal.server.readaudit.DefaultReadAuditPrepare; import io.ebeaninternal.server.text.json.DJsonContext; import io.ebeaninternal.server.transaction.AutoCommitTransactionManager; import io.ebeaninternal.server.transaction.DataSourceSupplier; import io.ebeaninternal.server.transaction.DefaultTransactionScopeManager; import io.ebeaninternal.server.transaction.DocStoreTransactionManager; import io.ebeaninternal.server.transaction.ExplicitTransactionManager; import io.ebeaninternal.server.transaction.ExternalTransactionScopeManager; import io.ebeaninternal.server.transaction.JtaTransactionManager; import io.ebeaninternal.server.transaction.TransactionManager; import io.ebeaninternal.server.transaction.TransactionManagerOptions; import io.ebeaninternal.server.transaction.TransactionScopeManager; import io.ebeaninternal.server.type.DefaultTypeManager; import io.ebeaninternal.server.type.TypeManager; import io.ebeanservice.docstore.api.DocStoreFactory; import io.ebeanservice.docstore.api.DocStoreIntegration; import io.ebeanservice.docstore.api.DocStoreUpdateProcessor; import io.ebeanservice.docstore.none.NoneDocStoreFactory; import com.fasterxml.jackson.core.JsonFactory; import org.avaje.datasource.DataSourcePool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.sql.DataSource; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.ServiceLoader; /** * Used to extend the ServerConfig with additional objects used to configure and * construct an EbeanServer. */ public class InternalConfiguration { private static final Logger logger = LoggerFactory.getLogger(InternalConfiguration.class); private final ServerConfig serverConfig; private final BootupClasses bootupClasses; private final DeployInherit deployInherit; private final TypeManager typeManager; private final DataTimeZone dataTimeZone; private final Binder binder; private final DeployCreateProperties deployCreateProperties; private final DeployUtil deployUtil; private final BeanDescriptorManager beanDescriptorManager; private final CQueryEngine cQueryEngine; private final ClusterManager clusterManager; private final SpiCacheManager cacheManager; private final ExpressionFactory expressionFactory; private final SpiBackgroundExecutor backgroundExecutor; private final JsonFactory jsonFactory; private final DocStoreFactory docStoreFactory; /** * List of plugins (that ultimately the DefaultServer configures late in construction). */ private final List<Plugin> plugins = new ArrayList<>(); public InternalConfiguration(ClusterManager clusterManager, SpiCacheManager cacheManager, SpiBackgroundExecutor backgroundExecutor, ServerConfig serverConfig, BootupClasses bootupClasses) { this.docStoreFactory = initDocStoreFactory(serverConfig.service(DocStoreFactory.class)); this.jsonFactory = serverConfig.getJsonFactory(); this.clusterManager = clusterManager; this.backgroundExecutor = backgroundExecutor; this.cacheManager = cacheManager; this.serverConfig = serverConfig; this.bootupClasses = bootupClasses; DatabasePlatform databasePlatform = serverConfig.getDatabasePlatform(); this.expressionFactory = initExpressionFactory(serverConfig, databasePlatform); this.typeManager = new DefaultTypeManager(serverConfig, bootupClasses); this.deployInherit = new DeployInherit(bootupClasses); this.deployCreateProperties = new DeployCreateProperties(typeManager); this.deployUtil = new DeployUtil(typeManager, serverConfig); this.beanDescriptorManager = new BeanDescriptorManager(this); Map<String, String> asOfTableMapping = beanDescriptorManager.deploy(); Map<String, String> draftTableMap = beanDescriptorManager.getDraftTableMap(); this.dataTimeZone = initDataTimeZone(); this.binder = getBinder(typeManager, databasePlatform, dataTimeZone); this.cQueryEngine = new CQueryEngine(serverConfig, databasePlatform, binder, asOfTableMapping, draftTableMap); } /** * Create and return the ExpressionFactory based on configuration and database platform. */ private ExpressionFactory initExpressionFactory(ServerConfig serverConfig, DatabasePlatform databasePlatform) { boolean nativeIlike = serverConfig.isExpressionNativeIlike() && databasePlatform.isSupportsNativeIlike(); return new DefaultExpressionFactory(serverConfig.isExpressionEqualsWithNullAsNoop(), nativeIlike); } private DocStoreFactory initDocStoreFactory(DocStoreFactory service) { return service == null ? new NoneDocStoreFactory() : service; } /** * Return the doc store factory. */ public DocStoreFactory getDocStoreFactory() { return docStoreFactory; } /** * Check if this is a SpiServerPlugin and if so 'collect' it to give the complete list * later on the DefaultServer for late call to configure(). */ public <T> T plugin(T maybePlugin) { if (maybePlugin instanceof Plugin) { plugins.add((Plugin) maybePlugin); } return maybePlugin; } /** * Return the list of plugins we collected during construction. */ public List<Plugin> getPlugins() { // find additional plugins via ServiceLoader ... for (Plugin plugin : ServiceLoader.load(Plugin.class)) { if (!plugins.contains(plugin)) { plugins.add(plugin); } } return plugins; } /** * Return the ChangeLogPrepare to use with a default implementation if none defined. */ public ChangeLogPrepare changeLogPrepare(ChangeLogPrepare prepare) { return plugin((prepare != null) ? prepare : new DefaultChangeLogPrepare()); } /** * Return the ChangeLogRegister to use with a default implementation if none defined. */ public ChangeLogRegister changeLogRegister(ChangeLogRegister register) { boolean includeInserts = serverConfig.isChangeLogIncludeInserts(); return plugin((register != null) ? register : new DefaultChangeLogRegister(includeInserts)); } /** * Return the ChangeLogListener to use with a default implementation if none defined. */ public ChangeLogListener changeLogListener(ChangeLogListener listener) { return plugin((listener != null) ? listener : new DefaultChangeLogListener()); } /** * Return the ReadAuditLogger implementation to use. */ public ReadAuditLogger getReadAuditLogger() { ReadAuditLogger found = bootupClasses.getReadAuditLogger(); return plugin(found != null ? found : new DefaultReadAuditLogger()); } /** * Return the ReadAuditPrepare implementation to use. */ public ReadAuditPrepare getReadAuditPrepare() { ReadAuditPrepare found = bootupClasses.getReadAuditPrepare(); return plugin(found != null ? found : new DefaultReadAuditPrepare()); } /** * For 'As Of' queries return the number of bind variables per predicate. */ private Binder getBinder(TypeManager typeManager, DatabasePlatform databasePlatform, DataTimeZone dataTimeZone) { DbExpressionHandler jsonHandler = getDbExpressionHandler(databasePlatform); DbHistorySupport historySupport = databasePlatform.getHistorySupport(); if (historySupport == null) { return new Binder(typeManager, 0, false, jsonHandler, dataTimeZone); } return new Binder(typeManager, historySupport.getBindCount(), historySupport.isStandardsBased(), jsonHandler, dataTimeZone); } /** * Return the JSON expression handler for the given database platform. */ private DbExpressionHandler getDbExpressionHandler(DatabasePlatform databasePlatform) { Platform platform = databasePlatform.getPlatform(); if (platform == Platform.POSTGRES) { return new PostgresJsonExpression(); } if (platform == Platform.ORACLE) { return new OracleDbExpression(); } return new NotSupportedDbExpression(); } public JsonContext createJsonContext(SpiEbeanServer server) { return new DJsonContext(server, jsonFactory, typeManager); } public AutoTuneService createAutoTuneService(SpiEbeanServer server) { return AutoTuneServiceFactory.create(server, serverConfig); } public RelationalQueryEngine createRelationalQueryEngine() { return new DefaultRelationalQueryEngine(binder, serverConfig.getDatabaseBooleanTrue()); } public OrmQueryEngine createOrmQueryEngine() { return new DefaultOrmQueryEngine(cQueryEngine); } public Persister createPersister(SpiEbeanServer server) { return new DefaultPersister(server, binder, beanDescriptorManager); } public SpiCacheManager getCacheManager() { return cacheManager; } public BootupClasses getBootupClasses() { return bootupClasses; } public DatabasePlatform getDatabasePlatform() { return serverConfig.getDatabasePlatform(); } public ServerConfig getServerConfig() { return serverConfig; } public ExpressionFactory getExpressionFactory() { return expressionFactory; } public Binder getBinder() { return binder; } public BeanDescriptorManager getBeanDescriptorManager() { return beanDescriptorManager; } public DeployInherit getDeployInherit() { return deployInherit; } public DeployCreateProperties getDeployCreateProperties() { return deployCreateProperties; } public DeployUtil getDeployUtil() { return deployUtil; } public CQueryEngine getCQueryEngine() { return cQueryEngine; } public SpiBackgroundExecutor getBackgroundExecutor() { return backgroundExecutor; } public GeneratedPropertyFactory getGeneratedPropertyFactory() { return new GeneratedPropertyFactory(serverConfig, bootupClasses.getIdGenerators()); } /** * Create the DocStoreIntegration components for the given server. */ public DocStoreIntegration createDocStoreIntegration(SpiServer server) { return plugin(docStoreFactory.create(server)); } /** * Create the TransactionManager taking into account autoCommit mode. */ public TransactionManager createTransactionManager(DocStoreUpdateProcessor indexUpdateProcessor) { boolean localL2 = cacheManager.isLocalL2Caching(); TransactionManagerOptions options = new TransactionManagerOptions(localL2, serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager, dataSource()); if (serverConfig.isExplicitTransactionBeginMode()) { return new ExplicitTransactionManager(options); } if (isAutoCommitMode()) { return new AutoCommitTransactionManager(options); } if (serverConfig.isDocStoreOnly()) { return new DocStoreTransactionManager(options); } return new TransactionManager(options); } /** * Return the DataSource supplier based on the tenancy mode. */ private DataSourceSupplier dataSource() { switch (serverConfig.getTenantMode()) { case DB: return new MultiTenantDbSupplier(serverConfig.getCurrentTenantProvider(), serverConfig.getTenantDataSourceProvider()); case SCHEMA: return new MultiTenantDbSchemaSupplier(serverConfig.getCurrentTenantProvider(), serverConfig.getDataSource(), serverConfig.getTenantSchemaProvider()); case CATALOG: return new MultiTenantDbCatalogSupplier(serverConfig.getCurrentTenantProvider(), serverConfig.getDataSource(), serverConfig.getTenantCatalogProvider()); default: return new SimpleDataSourceProvider(serverConfig.getDataSource()); } } /** * Return true if autoCommit mode is on. */ private boolean isAutoCommitMode() { if (serverConfig.isAutoCommitMode()) { // explicitly set return true; } DataSource dataSource = serverConfig.getDataSource(); return dataSource instanceof DataSourcePool && ((DataSourcePool) dataSource).isAutoCommit(); } /** * Create the TransactionScopeManager taking into account JTA or external transaction manager. */ public TransactionScopeManager createTransactionScopeManager(TransactionManager transactionManager) { ExternalTransactionManager externalTransactionManager = serverConfig.getExternalTransactionManager(); if (externalTransactionManager == null && serverConfig.isUseJtaTransactionManager()) { externalTransactionManager = new JtaTransactionManager(); } if (externalTransactionManager != null) { externalTransactionManager.setTransactionManager(transactionManager); logger.info("Using Transaction Manager [" + externalTransactionManager.getClass() + "]"); return new ExternalTransactionScopeManager(transactionManager, externalTransactionManager); } else { return new DefaultTransactionScopeManager(transactionManager); } } /** * Create the DataTimeZone implementation to use. */ private DataTimeZone initDataTimeZone() { String tz = serverConfig.getDataTimeZone(); if (tz == null) { return new NoDataTimeZone(); } if (getDatabasePlatform().getPlatform() == Platform.ORACLE) { return new CloneDataTimeZone(tz); } else { return new SimpleDataTimeZone(tz); } } public DataTimeZone getDataTimeZone() { return dataTimeZone; } public ServerCacheManager cache() { return new DefaultCacheAdapter(cacheManager); } }