diff --git a/build.gradle b/build.gradle index 2fb679f5ac..f5e36dc2f9 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,3 @@ -import org.apache.tools.ant.filters.ReplaceTokens - /* * Hibernate, Relational Persistence for Idiomatic Java * diff --git a/hibernate-core/src/main/java/org/hibernate/boot/cfgxml/spi/LoadedConfig.java b/hibernate-core/src/main/java/org/hibernate/boot/cfgxml/spi/LoadedConfig.java index f2e4f2f696..156f334d09 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/cfgxml/spi/LoadedConfig.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/cfgxml/spi/LoadedConfig.java @@ -47,7 +47,7 @@ public class LoadedConfig { private List mappingReferences; private Map> eventListenerMap; - private LoadedConfig(String sessionFactoryName) { + public LoadedConfig(String sessionFactoryName) { this.sessionFactoryName = sessionFactoryName; } @@ -259,7 +259,7 @@ public class LoadedConfig { } @SuppressWarnings("unchecked") - private void addConfigurationValues(Map configurationValues) { + protected void addConfigurationValues(Map configurationValues) { if ( configurationValues == null ) { return; } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/FilterSourceImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/FilterSourceImpl.java index 6569d928ab..ecdbe1d3f5 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/FilterSourceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/FilterSourceImpl.java @@ -14,6 +14,7 @@ import org.hibernate.boot.MappingException; import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmFilterAliasMappingType; import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmFilterType; import org.hibernate.boot.model.source.spi.FilterSource; +import org.hibernate.internal.util.NullnessHelper; import org.hibernate.internal.util.StringHelper; /** @@ -63,7 +64,7 @@ public class FilterSourceImpl } } - this.condition = Helper.coalesce( conditionContent, conditionAttribute ); + this.condition = NullnessHelper.coalesce( conditionContent, conditionAttribute ); this.autoAliasInjection = StringHelper.isNotEmpty( explicitAutoAliasInjectionSetting ) ? Boolean.valueOf( explicitAutoAliasInjectionSetting ) : true; diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/Helper.java b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/Helper.java index a4249a86ab..50a55e4e52 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/Helper.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/Helper.java @@ -123,33 +123,6 @@ public class Helper { return params; } - /** - * Operates like SQL coalesce expression, except empty strings are treated as null. Return the first non-empty value - * - * @param values The list of values. - * @param Generic type of values to coalesce - * - * @return The first non-empty value, or null if all values were empty - */ - public static T coalesce(T... values) { - if ( values == null ) { - return null; - } - for ( T value : values ) { - if ( value != null ) { - if ( String.class.isInstance( value ) ) { - if ( StringHelper.isNotEmpty( (String) value ) ) { - return value; - } - } - else { - return value; - } - } - } - return null; - } - static ToolingHintContext collectToolingHints( ToolingHintContext baseline, ToolingHintContainer toolingHintContainer) { diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/StandardServiceRegistryBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/StandardServiceRegistryBuilder.java index fe11cc0985..62c7393452 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/registry/StandardServiceRegistryBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/StandardServiceRegistryBuilder.java @@ -36,6 +36,35 @@ import org.hibernate.service.spi.ServiceContributor; * @see org.hibernate.boot.registry.BootstrapServiceRegistryBuilder */ public class StandardServiceRegistryBuilder { + /** + * Intended only for use from {@link org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl}. + * + * Creates a StandardServiceRegistryBuilder specific to the needs of JPA bootstrapping. + * Specifically we ignore properties found in `cfg.xml` files in terms of adding them to + * the builder immediately. EntityManagerFactoryBuilderImpl handles collecting these + * properties itself. + */ + public static StandardServiceRegistryBuilder forJpa(BootstrapServiceRegistry bootstrapServiceRegistry) { + final LoadedConfig loadedConfig = new LoadedConfig( null ) { + @Override + protected void addConfigurationValues(Map configurationValues) { + // here, do nothing + } + }; + return new StandardServiceRegistryBuilder( + bootstrapServiceRegistry, + new HashMap(), + loadedConfig + ) { + @Override + public StandardServiceRegistryBuilder configure(LoadedConfig loadedConfig) { + getAggregatedCfgXml().merge( loadedConfig ); + // super also collects the properties - here we skip that part + return this; + } + }; + } + /** * The default resource name for a hibernate configuration xml file. */ @@ -43,7 +72,7 @@ public class StandardServiceRegistryBuilder { private final Map settings; private final List initiators = standardInitiatorList(); - private final List providedServices = new ArrayList(); + private final List providedServices = new ArrayList<>(); private boolean autoCloseRegistry = true; @@ -67,6 +96,21 @@ public class StandardServiceRegistryBuilder { this( bootstrapServiceRegistry, LoadedConfig.baseline() ); } + /** + * Intended for use exclusively from JPA boot-strapping. + * + * @see #forJpa + */ + private StandardServiceRegistryBuilder( + BootstrapServiceRegistry bootstrapServiceRegistry, + Map settings, + LoadedConfig loadedConfig) { + this.bootstrapServiceRegistry = bootstrapServiceRegistry; + this.configLoader = new ConfigLoader( bootstrapServiceRegistry ); + this.settings = settings; + this.aggregatedCfgXml = loadedConfig; + } + /** * Create a builder with the specified bootstrap services. * @@ -81,6 +125,10 @@ public class StandardServiceRegistryBuilder { this.aggregatedCfgXml = loadedConfigBaseline; } + public ConfigLoader getConfigLoader() { + return configLoader; + } + /** * Intended for internal testing use only!! */ @@ -94,11 +142,12 @@ public class StandardServiceRegistryBuilder { * @return List of standard initiators */ private static List standardInitiatorList() { - final List initiators = new ArrayList(); + final List initiators = new ArrayList<>( StandardServiceInitiators.LIST.size() ); initiators.addAll( StandardServiceInitiators.LIST ); return initiators; } + @SuppressWarnings("unused") public BootstrapServiceRegistry getBootstrapServiceRegistry() { return bootstrapServiceRegistry; } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/NullnessHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/NullnessHelper.java new file mode 100644 index 0000000000..416257e60a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/NullnessHelper.java @@ -0,0 +1,60 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.internal.util; + +import java.util.function.Supplier; + +/** + * @author Steve Ebersole + */ +public class NullnessHelper { + + /** + * Operates like SQL coalesce expression, except empty strings are treated as null. Return the first non-empty value + * + * @param values The list of values. + * @param Generic type of values to coalesce + * + * @return The first non-empty value, or null if all values were empty + */ + public static T coalesce(T... values) { + if ( values == null ) { + return null; + } + for ( T value : values ) { + if ( value != null ) { + if ( String.class.isInstance( value ) ) { + if ( !( (String) value ).isEmpty() ) { + return value; + } + } + else { + return value; + } + } + } + return null; + } + + /** + * Find the first non-null value supplied by the given suppliers + */ + public static T coalesceSuppliedValues(Supplier... valueSuppliers) { + if ( valueSuppliers == null ) { + return null; + } + + for ( Supplier valueSupplier : valueSuppliers ) { + final T value = valueSupplier.get(); + if ( value != null ) { + return value; + } + } + + return null; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java index 965cf8c392..734b84d759 100644 --- a/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java @@ -27,11 +27,9 @@ import javax.sql.DataSource; import org.hibernate.SessionFactory; import org.hibernate.SessionFactoryObserver; import org.hibernate.boot.CacheRegionDefinition; -import org.hibernate.boot.MetadataBuilder; import org.hibernate.boot.MetadataSources; import org.hibernate.boot.SessionFactoryBuilder; import org.hibernate.boot.archive.scan.internal.StandardScanOptions; -import org.hibernate.boot.cfgxml.internal.ConfigLoader; import org.hibernate.boot.cfgxml.spi.CfgXmlAccessService; import org.hibernate.boot.cfgxml.spi.LoadedConfig; import org.hibernate.boot.cfgxml.spi.MappingReference; @@ -60,7 +58,9 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.id.factory.spi.MutableIdentifierGeneratorFactory; import org.hibernate.integrator.spi.Integrator; import org.hibernate.internal.EntityManagerMessageLogger; +import org.hibernate.internal.util.NullnessHelper; import org.hibernate.internal.util.StringHelper; +import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.jpa.AvailableSettings; import org.hibernate.jpa.boot.spi.EntityManagerFactoryBuilder; import org.hibernate.jpa.boot.spi.IntegratorProvider; @@ -73,6 +73,7 @@ import org.hibernate.jpa.spi.IdentifierGeneratorStrategyProvider; import org.hibernate.proxy.EntityNotFoundDelegate; import org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorBuilderImpl; import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl; +import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder; import org.hibernate.secure.spi.GrantedPermission; import org.hibernate.secure.spi.JaccPermissionDeclarations; import org.hibernate.service.ServiceRegistry; @@ -199,24 +200,31 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil final BootstrapServiceRegistry bsr = buildBootstrapServiceRegistry( integrationSettings, providedClassLoader, providedClassLoaderService); // merge configuration sources and build the "standard" service registry - final StandardServiceRegistryBuilder ssrBuilder = new StandardServiceRegistryBuilder( bsr ); + final StandardServiceRegistryBuilder ssrBuilder = StandardServiceRegistryBuilder.forJpa( bsr ); + final MergedSettings mergedSettings = mergeSettings( persistenceUnit, integrationSettings, ssrBuilder ); + + // flush before completion validation + if ( "true".equals( mergedSettings.configurationValues.get( Environment.FLUSH_BEFORE_COMPLETION ) ) ) { + LOG.definingFlushBeforeCompletionIgnoredInHem( Environment.FLUSH_BEFORE_COMPLETION ); + mergedSettings.configurationValues.put( Environment.FLUSH_BEFORE_COMPLETION, "false" ); + } + + // keep the merged config values for phase-2 this.configurationValues = mergedSettings.getConfigurationValues(); // Build the "standard" service registry ssrBuilder.applySettings( configurationValues ); - configure( ssrBuilder ); + this.standardServiceRegistry = ssrBuilder.build(); - configure( standardServiceRegistry, mergedSettings ); + + configureIdentifierGenerators( standardServiceRegistry ); final MetadataSources metadataSources = new MetadataSources( bsr ); - List attributeConverterDefinitions = populate( - metadataSources, - mergedSettings, - standardServiceRegistry - ); + List attributeConverterDefinitions = applyMappingResources( metadataSources ); + this.metamodelBuilder = (MetadataBuilderImplementor) metadataSources.getMetadataBuilder( standardServiceRegistry ); - populate( metamodelBuilder, mergedSettings, standardServiceRegistry, attributeConverterDefinitions ); + applyMetamodelBuilderSettings( mergedSettings, attributeConverterDefinitions ); // todo : would be nice to have MetadataBuilder still do the handling of CfgXmlAccessService here // another option is to immediately handle them here (probably in mergeSettings?) as we encounter them... @@ -308,7 +316,6 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // temporary! - @SuppressWarnings("unchecked") public Map getConfigurationValues() { return Collections.unmodifiableMap( configurationValues ); } @@ -327,7 +334,10 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil * @param associationManagementEnabled To enable association management feature * @return An enhancement context for classes managed by this EM */ - protected EnhancementContext getEnhancementContext(final boolean dirtyTrackingEnabled, final boolean lazyInitializationEnabled, final boolean associationManagementEnabled ) { + protected EnhancementContext getEnhancementContext( + final boolean dirtyTrackingEnabled, + final boolean lazyInitializationEnabled, + final boolean associationManagementEnabled ) { return new DefaultEnhancementContext() { @Override @@ -455,62 +465,25 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil return bsrBuilder.build(); } - @SuppressWarnings("unchecked") private MergedSettings mergeSettings( PersistenceUnitDescriptor persistenceUnit, Map integrationSettings, StandardServiceRegistryBuilder ssrBuilder) { final MergedSettings mergedSettings = new MergedSettings(); - - // first, apply persistence.xml-defined settings - if ( persistenceUnit.getProperties() != null ) { - mergedSettings.configurationValues.putAll( persistenceUnit.getProperties() ); - } - - mergedSettings.configurationValues.put( PERSISTENCE_UNIT_NAME, persistenceUnit.getName() ); - - final ConfigLoader configLoader = new ConfigLoader( ssrBuilder.getBootstrapServiceRegistry() ); + mergedSettings.processPersistenceUnitDescriptorProperties( persistenceUnit ); // see if the persistence.xml settings named a Hibernate config file.... - final String cfgXmlResourceName1 = (String) mergedSettings.configurationValues.remove( CFG_FILE ); - if ( StringHelper.isNotEmpty( cfgXmlResourceName1 ) ) { - final LoadedConfig loadedCfg = configLoader.loadConfigXmlResource( cfgXmlResourceName1 ); - processConfigXml( loadedCfg, mergedSettings, ssrBuilder ); + String cfgXmlResourceName = (String) mergedSettings.configurationValues.remove( CFG_FILE ); + if ( StringHelper.isEmpty( cfgXmlResourceName ) ) { + // see if integration settings named a Hibernate config file.... + cfgXmlResourceName = (String) integrationSettings.get( CFG_FILE ); } - // see if integration settings named a Hibernate config file.... - final String cfgXmlResourceName2 = (String) integrationSettings.get( CFG_FILE ); - if ( StringHelper.isNotEmpty( cfgXmlResourceName2 ) ) { - integrationSettings.remove( CFG_FILE ); - final LoadedConfig loadedCfg = configLoader.loadConfigXmlResource( cfgXmlResourceName2 ); - processConfigXml( loadedCfg, mergedSettings, ssrBuilder ); + if ( StringHelper.isNotEmpty( cfgXmlResourceName ) ) { + processHibernateConfigXmlResources( ssrBuilder, mergedSettings, cfgXmlResourceName ); } - // finally, apply integration-supplied settings (per JPA spec, integration settings should override other sources) - for ( Map.Entry entry : integrationSettings.entrySet() ) { - if ( entry.getKey() == null ) { - continue; - } - - if ( entry.getValue() == null ) { - mergedSettings.configurationValues.remove( entry.getKey() ); - } - else { - mergedSettings.configurationValues.put( entry.getKey(), entry.getValue() ); - } - } - - if ( !mergedSettings.configurationValues.containsKey( JPA_VALIDATION_MODE ) ) { - if ( persistenceUnit.getValidationMode() != null ) { - mergedSettings.configurationValues.put( JPA_VALIDATION_MODE, persistenceUnit.getValidationMode() ); - } - } - - if ( !mergedSettings.configurationValues.containsKey( JPA_SHARED_CACHE_MODE ) ) { - if ( persistenceUnit.getSharedCacheMode() != null ) { - mergedSettings.configurationValues.put( JPA_SHARED_CACHE_MODE, persistenceUnit.getSharedCacheMode() ); - } - } + normalizeSettings( persistenceUnit, integrationSettings, mergedSettings ); final String jaccContextId = (String) mergedSettings.configurationValues.get( JACC_CONTEXT_ID ); @@ -571,22 +544,446 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil return mergedSettings; } + /** + * Handles normalizing the settings coming from multiple sources, applying proper precedences + */ @SuppressWarnings("unchecked") - private void processConfigXml( - LoadedConfig loadedConfig, - MergedSettings mergedSettings, - StandardServiceRegistryBuilder ssrBuilder) { - if ( ! mergedSettings.configurationValues.containsKey( SESSION_FACTORY_NAME ) ) { - // there is not already a SF-name in the merged settings - final String sfName = loadedConfig.getSessionFactoryName(); - if ( sfName != null ) { - // but the cfg.xml file we are processing named one.. - mergedSettings.configurationValues.put( SESSION_FACTORY_NAME, sfName ); + private void normalizeSettings( + PersistenceUnitDescriptor persistenceUnit, + Map integrationSettings, + MergedSettings mergedSettings) { + // make a copy so we can remove things as we process them + final HashMap integrationSettingsCopy = new HashMap<>( integrationSettings ); + + normalizeConnectionAccessUserAndPass( integrationSettingsCopy, mergedSettings ); + + normalizeTransactionCoordinator( persistenceUnit, integrationSettingsCopy, mergedSettings ); + + normalizeDataAccess( integrationSettingsCopy, mergedSettings, persistenceUnit ); + + // normalize ValidationMode + final Object intgValidationMode = integrationSettingsCopy.remove( JPA_VALIDATION_MODE ); + if ( intgValidationMode != null ) { + mergedSettings.configurationValues.put( JPA_VALIDATION_MODE, intgValidationMode ); + } + else if ( persistenceUnit.getValidationMode() != null ) { + mergedSettings.configurationValues.put( JPA_VALIDATION_MODE, persistenceUnit.getValidationMode() ); + } + + // normalize SharedCacheMode + final Object intgCacheMode = integrationSettingsCopy.remove( JPA_SHARED_CACHE_MODE ); + if ( intgCacheMode != null ) { + mergedSettings.configurationValues.put( JPA_SHARED_CACHE_MODE, intgCacheMode ); + } + else if ( persistenceUnit.getSharedCacheMode() != null ) { + mergedSettings.configurationValues.put( JPA_SHARED_CACHE_MODE, persistenceUnit.getSharedCacheMode() ); + } + + // Apply all "integration overrides" as the last step. By specification, + // these should have precedence. + // + // NOTE that this occurs after the specialized normalize calls above which remove + // any specially-handled settings. + for ( Map.Entry entry : integrationSettingsCopy.entrySet() ) { + if ( entry.getKey() == null ) { + continue; + } + + if ( entry.getValue() == null ) { + mergedSettings.configurationValues.remove( entry.getKey() ); + } + else { + mergedSettings.configurationValues.put( entry.getKey(), entry.getValue() ); + } + } + } + + /** + * Because a DataSource can be secured (requiring Hibernate to pass the USER/PASSWORD when accessing the DataSource) + * we apply precedence to the USER and PASS separately + */ + private void normalizeConnectionAccessUserAndPass( + HashMap integrationSettingsCopy, + MergedSettings mergedSettings) { + //noinspection unchecked + final Object effectiveUser = NullnessHelper.coalesceSuppliedValues( + () -> integrationSettingsCopy.remove( USER ), + () -> integrationSettingsCopy.remove( JPA_JDBC_USER ), + () -> extractPuProperty( persistenceUnit, USER ), + () -> extractPuProperty( persistenceUnit, JPA_JDBC_USER ) + ); + + //noinspection unchecked + final Object effectivePass = NullnessHelper.coalesceSuppliedValues( + () -> integrationSettingsCopy.remove( PASS ), + () -> integrationSettingsCopy.remove( JPA_JDBC_PASSWORD ), + () -> extractPuProperty( persistenceUnit, PASS ), + () -> extractPuProperty( persistenceUnit, JPA_JDBC_PASSWORD ) + ); + + if ( effectiveUser != null || effectivePass != null ) { + applyUserAndPass( effectiveUser, effectivePass, mergedSettings ); + } + } + + private T extractPuProperty(PersistenceUnitDescriptor persistenceUnit, String propertyName) { + //noinspection unchecked + return persistenceUnit.getProperties() == null ? null : (T) persistenceUnit.getProperties().get( propertyName ); + } + + @SuppressWarnings("unchecked") + private void applyUserAndPass(Object effectiveUser, Object effectivePass, MergedSettings mergedSettings) { + if ( effectiveUser != null ) { + mergedSettings.configurationValues.put( USER, effectiveUser ); + mergedSettings.configurationValues.put( JPA_JDBC_USER, effectiveUser ); + } + + if ( effectivePass != null ) { + mergedSettings.configurationValues.put( PASS, effectivePass ); + mergedSettings.configurationValues.put( JPA_JDBC_PASSWORD, effectivePass ); + } + } + + private static final String IS_JTA_TXN_COORD = "local.setting.IS_JTA_TXN_COORD"; + + @SuppressWarnings("unchecked") + private void normalizeTransactionCoordinator( + PersistenceUnitDescriptor persistenceUnit, + HashMap integrationSettingsCopy, + MergedSettings mergedSettings) { + PersistenceUnitTransactionType txnType = null; + + final Object intgTxnType = integrationSettingsCopy.remove( JPA_TRANSACTION_TYPE ); + + if ( intgTxnType != null ) { + txnType = PersistenceUnitTransactionTypeHelper.interpretTransactionType( intgTxnType ); + } + else if ( persistenceUnit.getTransactionType() != null ) { + txnType = persistenceUnit.getTransactionType(); + } + else { + final Object puPropTxnType = mergedSettings.configurationValues.get( JPA_TRANSACTION_TYPE ); + if ( puPropTxnType != null ) { + txnType = PersistenceUnitTransactionTypeHelper.interpretTransactionType( puPropTxnType ); } } - mergedSettings.configurationValues.putAll( loadedConfig.getConfigurationValues() ); - ssrBuilder.configure( loadedConfig ); + if ( txnType == null ) { + // is it more appropriate to have this be based on bootstrap entry point (EE vs SE)? + LOG.debugf( "PersistenceUnitTransactionType not specified - falling back to RESOURCE_LOCAL" ); + txnType = PersistenceUnitTransactionType.RESOURCE_LOCAL; + } + + boolean hasTxStrategy = mergedSettings.configurationValues.containsKey( TRANSACTION_COORDINATOR_STRATEGY ); + final Boolean definiteJtaCoordinator; + + if ( hasTxStrategy ) { + LOG.overridingTransactionStrategyDangerous( TRANSACTION_COORDINATOR_STRATEGY ); + + // see if we can tell whether it is a JTA coordinator + final Object strategy = mergedSettings.configurationValues.get( TRANSACTION_COORDINATOR_STRATEGY ); + if ( strategy instanceof TransactionCoordinatorBuilder ) { + definiteJtaCoordinator = ( (TransactionCoordinatorBuilder) strategy ).isJta(); + } + else { + definiteJtaCoordinator = false; + } + } + else { + if ( txnType == PersistenceUnitTransactionType.JTA ) { + mergedSettings.configurationValues.put( TRANSACTION_COORDINATOR_STRATEGY, JtaTransactionCoordinatorBuilderImpl.class ); + definiteJtaCoordinator = true; + } + else if ( txnType == PersistenceUnitTransactionType.RESOURCE_LOCAL ) { + mergedSettings.configurationValues.put( TRANSACTION_COORDINATOR_STRATEGY, JdbcResourceLocalTransactionCoordinatorBuilderImpl.class ); + definiteJtaCoordinator = false; + } + else { + throw new IllegalStateException( "Could not determine TransactionCoordinator strategy to use" ); + } + } + + mergedSettings.configurationValues.put( IS_JTA_TXN_COORD, definiteJtaCoordinator ); + } + + private void normalizeDataAccess( + HashMap integrationSettingsCopy, + MergedSettings mergedSettings, + PersistenceUnitDescriptor persistenceUnit) { + if ( dataSource != null ) { + applyDataSource( + dataSource, + // we don't explicitly know + null, + integrationSettingsCopy, + mergedSettings + ); + + // EARLY EXIT!! + return; + } + + if ( integrationSettingsCopy.containsKey( DATASOURCE ) ) { + final Object dataSourceRef = integrationSettingsCopy.remove( DATASOURCE ); + if ( dataSourceRef != null ) { + applyDataSource( + dataSourceRef, + null, + integrationSettingsCopy, + mergedSettings + ); + + // EARLY EXIT!! + return; + } + } + + if ( integrationSettingsCopy.containsKey( JPA_JTA_DATASOURCE ) ) { + final Object dataSourceRef = integrationSettingsCopy.remove( JPA_JTA_DATASOURCE ); + if ( dataSourceRef != null ) { + applyDataSource( + dataSourceRef, + true, + integrationSettingsCopy, + mergedSettings + ); + + // EARLY EXIT!! + return; + } + } + + if ( integrationSettingsCopy.containsKey( JPA_NON_JTA_DATASOURCE ) ) { + final Object dataSourceRef = integrationSettingsCopy.remove( JPA_NON_JTA_DATASOURCE ); + + applyDataSource( + dataSourceRef, + false, + integrationSettingsCopy, + mergedSettings + ); + + // EARLY EXIT!! + return; + } + + if ( integrationSettingsCopy.containsKey( URL ) ) { + // these have precedence over the JPA ones + final Object integrationJdbcUrl = integrationSettingsCopy.get( URL ); + if ( integrationJdbcUrl != null ) { + //noinspection unchecked + applyJdbcSettings( + integrationJdbcUrl, + NullnessHelper.coalesceSuppliedValues( + () -> ConfigurationHelper.getString( DRIVER, integrationSettingsCopy ), + () -> ConfigurationHelper.getString( JPA_JDBC_DRIVER, integrationSettingsCopy ), + () -> ConfigurationHelper.getString( DRIVER, mergedSettings.configurationValues ), + () -> ConfigurationHelper.getString( JPA_JDBC_DRIVER, mergedSettings.configurationValues ) + ), + integrationSettingsCopy, + mergedSettings + ); + + // EARLY EXIT!! + return; + } + } + + if ( integrationSettingsCopy.containsKey( JPA_JDBC_URL ) ) { + final Object integrationJdbcUrl = integrationSettingsCopy.get( JPA_JDBC_URL ); + + if ( integrationJdbcUrl != null ) { + //noinspection unchecked + applyJdbcSettings( + integrationJdbcUrl, + NullnessHelper.coalesceSuppliedValues( + () -> ConfigurationHelper.getString( JPA_JDBC_DRIVER, integrationSettingsCopy ), + () -> ConfigurationHelper.getString( JPA_JDBC_DRIVER, mergedSettings.configurationValues ) + ), + integrationSettingsCopy, + mergedSettings + ); + + // EARLY EXIT!! + return; + } + } + + if ( persistenceUnit.getJtaDataSource() != null ) { + applyDataSource( + persistenceUnit.getJtaDataSource(), + true, + integrationSettingsCopy, + mergedSettings + ); + + // EARLY EXIT!! + return; + } + + if ( persistenceUnit.getNonJtaDataSource() != null ) { + applyDataSource( + persistenceUnit.getNonJtaDataSource(), + false, + integrationSettingsCopy, + mergedSettings + ); + + // EARLY EXIT!! + return; + } + + if ( mergedSettings.configurationValues.containsKey( URL ) ) { + final Object url = mergedSettings.configurationValues.get( URL ); + + if ( url != null && ( ! ( url instanceof String ) || StringHelper.isNotEmpty( (String) url ) ) ) { + applyJdbcSettings( + url, + ConfigurationHelper.getString( DRIVER, mergedSettings.configurationValues ), + integrationSettingsCopy, + mergedSettings + ); + + // EARLY EXIT!! + return; + } + } + + if ( mergedSettings.configurationValues.containsKey( JPA_JDBC_URL ) ) { + final Object url = mergedSettings.configurationValues.get( JPA_JDBC_URL ); + + if ( url != null && ( ! ( url instanceof String ) || StringHelper.isNotEmpty( (String) url ) ) ) { + applyJdbcSettings( + url, + ConfigurationHelper.getString( JPA_JDBC_DRIVER, mergedSettings.configurationValues ), + integrationSettingsCopy, + mergedSettings + ); + + // EARLY EXIT!! + return; + } + } + + // any other conditions to account for? + } + + @SuppressWarnings("unchecked") + private void applyDataSource( + Object dataSourceRef, + Boolean useJtaDataSource, + HashMap integrationSettingsCopy, + MergedSettings mergedSettings) { + + // `IS_JTA_TXN_COORD` is a value set during `#normalizeTransactionCoordinator` to indicate whether + // the execution environment "is JTA" as best as it can tell.. + // + // we use this value when JTA was not explicitly specified in regards the DataSource + final boolean isJtaTransactionCoordinator = (boolean) mergedSettings.configurationValues.remove( IS_JTA_TXN_COORD ); + final boolean isJta = useJtaDataSource == null ? isJtaTransactionCoordinator : useJtaDataSource; + + // add to EMF properties (questionable - see HHH-13432) + final String emfKey; + final String inverseEmfKey; + if ( isJta ) { + emfKey = JPA_JTA_DATASOURCE; + inverseEmfKey = JPA_NON_JTA_DATASOURCE; + } + else { + emfKey = JPA_NON_JTA_DATASOURCE; + inverseEmfKey = JPA_JTA_DATASOURCE; + } + mergedSettings.configurationValues.put( emfKey, dataSourceRef ); + + // clear any settings logically overridden by this datasource + cleanUpConfigKeys( + integrationSettingsCopy, + mergedSettings, + inverseEmfKey, + JPA_JDBC_DRIVER, + DRIVER, + JPA_JDBC_URL, + URL + ); + + + // clean-up the entries in the "integration overrides" so they do not get get picked + // up in the general "integration overrides" handling + cleanUpConfigKeys( integrationSettingsCopy, DATASOURCE, JPA_JTA_DATASOURCE, JPA_NON_JTA_DATASOURCE ); + + // add under Hibernate's DATASOURCE setting where the ConnectionProvider will find it + mergedSettings.configurationValues.put( DATASOURCE, dataSourceRef ); + } + + private void cleanUpConfigKeys(HashMap integrationSettingsCopy, MergedSettings mergedSettings, String... keys) { + for ( String key : keys ) { + final Object removedIntgSetting = integrationSettingsCopy.remove( key ); + if ( removedIntgSetting != null ) { + LOG.debugf( "Removed integration override setting [%s] due to normalization", key ); + } + + final Object removedMergedSetting = mergedSettings.configurationValues.remove( key ); + if ( removedMergedSetting != null ) { + LOG.debugf( "Removed merged setting [%s] due to normalization", key ); + } + } + } + + private void cleanUpConfigKeys(Map settings, String... keys) { + for ( String key : keys ) { + settings.remove( key ); + } + } + + @SuppressWarnings("unchecked") + private void applyJdbcSettings( + Object url, + String driver, + HashMap integrationSettingsCopy, + MergedSettings mergedSettings) { + mergedSettings.configurationValues.put( URL, url ); + mergedSettings.configurationValues.put( JPA_JDBC_URL, url ); + + if ( driver != null ) { + mergedSettings.configurationValues.put( DRIVER, driver ); + mergedSettings.configurationValues.put( JPA_JDBC_DRIVER, driver ); + } + else { + mergedSettings.configurationValues.remove( DRIVER ); + mergedSettings.configurationValues.remove( JPA_JDBC_DRIVER ); + } + + // clean up the integration-map values + cleanUpConfigKeys( + integrationSettingsCopy, + DRIVER, + JPA_JDBC_DRIVER, + URL, + JPA_JDBC_URL, + USER, + JPA_JDBC_USER, + PASS, + JPA_JDBC_PASSWORD + ); + + cleanUpConfigKeys( + integrationSettingsCopy, + mergedSettings, + DATASOURCE, + JPA_JTA_DATASOURCE, + JPA_NON_JTA_DATASOURCE + ); + } + + private void processHibernateConfigXmlResources( + StandardServiceRegistryBuilder ssrBuilder, + MergedSettings mergedSettings, + String cfgXmlResourceName) { + final LoadedConfig loadedConfig = ssrBuilder.getConfigLoader().loadConfigXmlResource( cfgXmlResourceName ); + + mergedSettings.processHibernateConfigXmlResources( loadedConfig ); + + ssrBuilder.getAggregatedCfgXml().merge( loadedConfig ); } private GrantedPermission parseJaccConfigEntry(String keyString, String valueString) { @@ -642,90 +1039,7 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil return new CacheRegionDefinition( cacheType, role, usage, region, lazyProperty ); } - private void configure(StandardServiceRegistryBuilder ssrBuilder) { - - applyJdbcConnectionProperties( ssrBuilder ); - applyTransactionProperties( ssrBuilder ); - - // flush before completion validation - if ( "true".equals( configurationValues.get( Environment.FLUSH_BEFORE_COMPLETION ) ) ) { - ssrBuilder.applySetting( Environment.FLUSH_BEFORE_COMPLETION, "false" ); - LOG.definingFlushBeforeCompletionIgnoredInHem( Environment.FLUSH_BEFORE_COMPLETION ); - } - -// final StrategySelector strategySelector = ssrBuilder.getBootstrapServiceRegistry().getService( StrategySelector.class ); -// final Object interceptorSetting = configurationValues.remove( AvailableSettings.SESSION_INTERCEPTOR ); -// if ( interceptorSetting != null ) { -// settings.setSessionInterceptorClass( -// loadSessionInterceptorClass( interceptorSetting, strategySelector ) -// ); -// } - } - - private void applyJdbcConnectionProperties(StandardServiceRegistryBuilder ssrBuilder) { - if ( dataSource != null ) { - ssrBuilder.applySetting( DATASOURCE, dataSource ); - } - else if ( persistenceUnit.getJtaDataSource() != null ) { - if ( ! ssrBuilder.getSettings().containsKey( DATASOURCE ) ) { - ssrBuilder.applySetting( DATASOURCE, persistenceUnit.getJtaDataSource() ); - // HHH-8121 : make the PU-defined value available to EMF.getProperties() - configurationValues.put( JPA_JTA_DATASOURCE, persistenceUnit.getJtaDataSource() ); - } - } - else if ( persistenceUnit.getNonJtaDataSource() != null ) { - if ( ! ssrBuilder.getSettings().containsKey( DATASOURCE ) ) { - ssrBuilder.applySetting( DATASOURCE, persistenceUnit.getNonJtaDataSource() ); - // HHH-8121 : make the PU-defined value available to EMF.getProperties() - configurationValues.put( JPA_NON_JTA_DATASOURCE, persistenceUnit.getNonJtaDataSource() ); - } - } - else { - final String driver = (String) configurationValues.get( JPA_JDBC_DRIVER ); - if ( StringHelper.isNotEmpty( driver ) ) { - ssrBuilder.applySetting( DRIVER, driver ); - } - final String url = (String) configurationValues.get( JPA_JDBC_URL ); - if ( StringHelper.isNotEmpty( url ) ) { - ssrBuilder.applySetting( URL, url ); - } - final String user = (String) configurationValues.get( JPA_JDBC_USER ); - if ( StringHelper.isNotEmpty( user ) ) { - ssrBuilder.applySetting( USER, user ); - } - final String pass = (String) configurationValues.get( JPA_JDBC_PASSWORD ); - if ( StringHelper.isNotEmpty( pass ) ) { - ssrBuilder.applySetting( PASS, pass ); - } - } - } - - private void applyTransactionProperties(StandardServiceRegistryBuilder ssrBuilder) { - PersistenceUnitTransactionType txnType = PersistenceUnitTransactionTypeHelper.interpretTransactionType( - configurationValues.get( JPA_TRANSACTION_TYPE ) - ); - if ( txnType == null ) { - txnType = persistenceUnit.getTransactionType(); - } - if ( txnType == null ) { - // is it more appropriate to have this be based on bootstrap entry point (EE vs SE)? - txnType = PersistenceUnitTransactionType.RESOURCE_LOCAL; - } - boolean hasTxStrategy = configurationValues.containsKey( TRANSACTION_COORDINATOR_STRATEGY ); - if ( hasTxStrategy ) { - LOG.overridingTransactionStrategyDangerous( TRANSACTION_COORDINATOR_STRATEGY ); - } - else { - if ( txnType == PersistenceUnitTransactionType.JTA ) { - ssrBuilder.applySetting( TRANSACTION_COORDINATOR_STRATEGY, JtaTransactionCoordinatorBuilderImpl.class ); - } - else if ( txnType == PersistenceUnitTransactionType.RESOURCE_LOCAL ) { - ssrBuilder.applySetting( TRANSACTION_COORDINATOR_STRATEGY, JdbcResourceLocalTransactionCoordinatorBuilderImpl.class ); - } - } - } - - private void configure(StandardServiceRegistry ssr, MergedSettings mergedSettings) { + private void configureIdentifierGenerators(StandardServiceRegistry ssr) { final StrategySelector strategySelector = ssr.getService( StrategySelector.class ); // apply id generators @@ -747,10 +1061,9 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil } @SuppressWarnings("unchecked") - protected List populate( - MetadataSources metadataSources, - MergedSettings mergedSettings, - StandardServiceRegistry ssr) { + private List applyMappingResources(MetadataSources metadataSources) { + // todo : where in the heck are `org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor.getManagedClassNames` handled?!? + // final ClassLoaderService classLoaderService = ssr.getService( ClassLoaderService.class ); // // // todo : make sure MetadataSources/Metadata are capable of handling duplicate sources @@ -827,12 +1140,10 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil return attributeConverterDefinitions; } - protected void populate( - MetadataBuilder metamodelBuilder, + private void applyMetamodelBuilderSettings( MergedSettings mergedSettings, - StandardServiceRegistry ssr, List attributeConverterDefinitions) { - ( (MetadataBuilderImplementor) metamodelBuilder ).getBootstrapContext().markAsJpaBootstrap(); + metamodelBuilder.getBootstrapContext().markAsJpaBootstrap(); if ( persistenceUnit.getTempClassLoader() != null ) { metamodelBuilder.applyTempClassLoader( persistenceUnit.getTempClassLoader() ); @@ -916,7 +1227,7 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil // Metamodel will clean this up... try { SessionFactoryBuilder sfBuilder = metadata().getSessionFactoryBuilder(); - populate( sfBuilder, standardServiceRegistry ); + populateSfBuilder( sfBuilder, standardServiceRegistry ); SchemaManagementToolCoordinator.process( metadata, standardServiceRegistry, configurationValues, DelayedDropRegistryNotAvailableImpl.INSTANCE @@ -930,10 +1241,10 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil cancel(); } - @SuppressWarnings("unchecked") + @Override public EntityManagerFactory build() { - SessionFactoryBuilder sfBuilder = metadata().getSessionFactoryBuilder(); - populate( sfBuilder, standardServiceRegistry ); + final SessionFactoryBuilder sfBuilder = metadata().getSessionFactoryBuilder(); + populateSfBuilder( sfBuilder, standardServiceRegistry ); try { return sfBuilder.build(); @@ -943,7 +1254,7 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil } } - protected void populate(SessionFactoryBuilder sfBuilder, StandardServiceRegistry ssr) { + protected void populateSfBuilder(SessionFactoryBuilder sfBuilder, StandardServiceRegistry ssr) { final StrategySelector strategySelector = ssr.getService( StrategySelector.class ); @@ -1028,7 +1339,36 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil private Map jaccPermissionsByContextId; private List cacheRegionDefinitions; + /** + * MergedSettings is initialized with hibernate.properties + */ private MergedSettings() { + configurationValues.putAll( Environment.getProperties() ); + } + + public void processPersistenceUnitDescriptorProperties(PersistenceUnitDescriptor persistenceUnit) { + if ( persistenceUnit.getProperties() != null ) { + configurationValues.putAll( persistenceUnit.getProperties() ); + } + + configurationValues.put( PERSISTENCE_UNIT_NAME, persistenceUnit.getName() ); + + } + + public void processHibernateConfigXmlResources(LoadedConfig loadedConfig){ + if ( ! configurationValues.containsKey( SESSION_FACTORY_NAME ) ) { + // there is not already a SF-name in the merged settings + final String sfName = loadedConfig.getSessionFactoryName(); + if ( sfName != null ) { + // but the cfg.xml file we are processing named one.. + configurationValues.put( SESSION_FACTORY_NAME, sfName ); + } + } + else { + // make sure they match? + } + + configurationValues.putAll( loadedConfig.getConfigurationValues() ); } public Map getConfigurationValues() { diff --git a/hibernate-core/src/test/java/org/hibernate/internal/MaskSensitiveInformationTest.java b/hibernate-core/src/test/java/org/hibernate/internal/MaskSensitiveInformationTest.java index 4cd9a4305b..1e0c63982f 100644 --- a/hibernate-core/src/test/java/org/hibernate/internal/MaskSensitiveInformationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/internal/MaskSensitiveInformationTest.java @@ -34,10 +34,8 @@ public class MaskSensitiveInformationTest extends BaseEntityManagerFunctionalTes } @Override - @SuppressWarnings("unchecked") protected void addConfigOptions(Map options) { - options.put( AvailableSettings.JPA_JDBC_USER, options.get( AvailableSettings.USER ) ); - options.put( AvailableSettings.JPA_JDBC_PASSWORD, options.get( AvailableSettings.PASS ) ); + super.addConfigOptions( options ); } @Test diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/connection/DataSourceInjectionTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/connection/DataSourceInjectionTest.java index 8a113e016f..8c1125218c 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/connection/DataSourceInjectionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/connection/DataSourceInjectionTest.java @@ -9,53 +9,106 @@ package org.hibernate.jpa.test.connection; -import java.io.File; -import javax.persistence.EntityManagerFactory; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; import javax.persistence.PersistenceException; +import javax.sql.DataSource; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.dialect.H2Dialect; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.jpa.HibernatePersistenceProvider; +import org.hibernate.jpa.test.Distributor; +import org.hibernate.jpa.test.Item; +import org.hibernate.jpa.test.xml.Light; +import org.hibernate.jpa.test.xml.Lighter; +import org.hibernate.testing.util.jpa.PersistenceUnitInfoAdapter; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; /** * @author Emmanuel Bernard */ public class DataSourceInjectionTest { - EntityManagerFactory emf; @Test public void testDatasourceInjection() throws Exception { - File current = new File("."); - File sub = new File(current, "puroot"); - sub.mkdir(); - PersistenceUnitInfoImpl info = new PersistenceUnitInfoImpl( sub.toURI().toURL(), new String[]{} ); + withPuRoot( + puRootUrl -> { + final PersistenceUnitInfoAdapter persistenceUnitInfo = createPuDescriptor( puRootUrl, new FakeDataSource() ); + + // otherwise the FakeDataSourceException will be eaten trying to resolve the Dialect + final Map intgOverrides = Collections.singletonMap( + AvailableSettings.DIALECT, + H2Dialect.class + ); + + final HibernatePersistenceProvider provider = new HibernatePersistenceProvider(); + try ( final SessionFactoryImplementor sf = provider.createContainerEntityManagerFactory( + persistenceUnitInfo, + intgOverrides + ).unwrap( SessionFactoryImplementor.class ) ) { + + try ( final SessionImplementor session = sf.openSession().unwrap( SessionImplementor.class ) ) { + session.createQuery( "select i from Item i" ).list(); + Assert.fail( "Expecting FakeDataSourceException" ); + } + catch (PersistenceException pe) { + try { + throw (RuntimeException) pe.getCause(); + } + catch (FakeDataSourceException fde) { + //success + } + } + catch (FakeDataSourceException fde) { + //success + } + } + } + ); + } + + protected PersistenceUnitInfoAdapter createPuDescriptor(URL puRootUrl, DataSource dataSource) { + return new PersistenceUnitInfoAdapter() { + @Override + public DataSource getNonJtaDataSource() { + return dataSource; + } + + @Override + public URL getPersistenceUnitRootUrl() { + return puRootUrl; + } + + public List getManagedClassNames() { + List classes = new ArrayList<>(); + classes.add( Item.class.getName() ); + classes.add( Distributor.class.getName() ); + classes.add( Light.class.getName() ); + classes.add( Lighter.class.getName() ); + return classes; + } + }; + } + + private void withPuRoot(Consumer puRootUrlConsumer) throws Exception { + // create a temporary directory to serve as the "PU root URL" + final Path puroot = Files.createTempDirectory( "puroot" ); + final URL puRootUrl = puroot.toUri().toURL(); + try { - emf = new HibernatePersistenceProvider().createContainerEntityManagerFactory( info, null ); - try { - emf.createEntityManager().createQuery( "select i from Item i" ).getResultList(); - } - finally { - try { - emf.close(); - } - catch (Exception ignore) { - int i = 0; - } - } - Assert.fail( "FakeDatasource should have been used" ); - } - catch (PersistenceException pe) { - if(emf != null){ - emf.close(); - } - Assert.assertTrue( pe.getCause() instanceof FakeDataSourceException ); - } - catch (FakeDataSourceException fde) { - //success + puRootUrlConsumer.accept( puRootUrl ); } finally { - sub.delete(); + Files.deleteIfExists( puroot ); } } } diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/connection/PersistenceUnitInfoImpl.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/connection/PersistenceUnitInfoImpl.java index cec9460c97..8e8ed9c0e2 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/connection/PersistenceUnitInfoImpl.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/connection/PersistenceUnitInfoImpl.java @@ -37,7 +37,7 @@ public class PersistenceUnitInfoImpl implements PersistenceUnitInfo { private URL puRoot; public PersistenceUnitInfoImpl(URL puRoot, String[] mappingFiles) { - this.mappingFiles = new ArrayList( mappingFiles.length ); + this.mappingFiles = new ArrayList<>( mappingFiles.length ); this.mappingFiles.addAll( Arrays.asList( mappingFiles ) ); this.puRoot = puRoot; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/SchemaToolingAutoActionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/SchemaToolingAutoActionTests.java new file mode 100644 index 0000000000..cc581d3346 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/SchemaToolingAutoActionTests.java @@ -0,0 +1,34 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.bootstrap; + +import java.util.Properties; + +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.tool.schema.Action; +import org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * @author Steve Ebersole + */ +public class SchemaToolingAutoActionTests { + @Test + public void testLegacySettingAsAction() { + final Properties props = new Properties(); + props.put( AvailableSettings.HBM2DDL_AUTO, Action.CREATE_DROP ); + + final SchemaManagementToolCoordinator.ActionGrouping actionGrouping = SchemaManagementToolCoordinator.ActionGrouping.interpret( props ); + + assertThat( actionGrouping.getDatabaseAction(), is( Action.CREATE_DROP ) ); + + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/jpa/PersistenceUnitInfoTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/jpa/PersistenceUnitInfoTests.java new file mode 100644 index 0000000000..1c0ca45444 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/jpa/PersistenceUnitInfoTests.java @@ -0,0 +1,105 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.bootstrap.jpa; + +import java.util.Collections; +import java.util.Map; +import javax.persistence.EntityManagerFactory; +import javax.persistence.spi.PersistenceProvider; +import javax.sql.DataSource; + +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.jpa.HibernatePersistenceProvider; +import org.hibernate.testing.jdbc.DataSourceStub; + +import org.hibernate.testing.FailureExpected; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseUnitTestCase; +import org.hibernate.testing.util.jpa.PersistenceUnitInfoAdapter; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; + +/** + * @author Steve Ebersole + */ +public class PersistenceUnitInfoTests extends BaseUnitTestCase { + @Test + @TestForIssue( jiraKey = "HHH-13432" ) + public void testNonJtaDataExposedAsProperty() { + final DataSource puDataSource = new DataSourceStub( "puDataSource" ); + final PersistenceUnitInfoAdapter info = new PersistenceUnitInfoAdapter() { + + @Override + public DataSource getNonJtaDataSource() { + return puDataSource; + } + }; + + final PersistenceProvider provider = new HibernatePersistenceProvider(); + + final EntityManagerFactory emf = provider.createContainerEntityManagerFactory( + info, + Collections.emptyMap() + ); + + // first let's check the DataSource used in the EMF... + final ConnectionProvider connectionProvider = emf.unwrap( SessionFactoryImplementor.class ) + .getServiceRegistry() + .getService( ConnectionProvider.class ); + assertThat( connectionProvider, instanceOf( DatasourceConnectionProviderImpl.class ) ); + final DatasourceConnectionProviderImpl dsCp = (DatasourceConnectionProviderImpl) connectionProvider; + assertThat( dsCp.getDataSource(), is( puDataSource ) ); + + // now let's check that it is exposed via the EMF properties + // - note : the spec does not indicate that this should work, but + // it worked this way in previous versions + final Object o = emf.getProperties().get( AvailableSettings.JPA_NON_JTA_DATASOURCE ); + assertThat( o, is( puDataSource ) ); + } + + @Test + @TestForIssue( jiraKey = "HHH-13432" ) + public void testJtaDataExposedAsProperty() { + final DataSource puDataSource = new DataSourceStub( "puDataSource" ); + final PersistenceUnitInfoAdapter info = new PersistenceUnitInfoAdapter() { + + @Override + public DataSource getJtaDataSource() { + return puDataSource; + } + }; + + final PersistenceProvider provider = new HibernatePersistenceProvider(); + + final EntityManagerFactory emf = provider.createContainerEntityManagerFactory( + info, + Collections.emptyMap() + ); + + // first let's check the DataSource used in the EMF... + final ConnectionProvider connectionProvider = emf.unwrap( SessionFactoryImplementor.class ) + .getServiceRegistry() + .getService( ConnectionProvider.class ); + assertThat( connectionProvider, instanceOf( DatasourceConnectionProviderImpl.class ) ); + final DatasourceConnectionProviderImpl dsCp = (DatasourceConnectionProviderImpl) connectionProvider; + assertThat( dsCp.getDataSource(), is( puDataSource ) ); + + // now let's check that it is exposed via the EMF properties + // - again, the spec does not indicate that this should work, but + // it worked this way in previous versions + final Map properties = emf.getProperties(); + final Object o = properties.get( AvailableSettings.JPA_JTA_DATASOURCE ); + assertEquals( puDataSource, o ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/jpa/PersistenceUnitOverridesTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/jpa/PersistenceUnitOverridesTests.java new file mode 100644 index 0000000000..a40e0c9868 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/jpa/PersistenceUnitOverridesTests.java @@ -0,0 +1,370 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.bootstrap.jpa; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import javax.persistence.EntityManagerFactory; +import javax.persistence.spi.PersistenceProvider; +import javax.persistence.spi.PersistenceUnitInfo; +import javax.sql.DataSource; + +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.jpa.HibernatePersistenceProvider; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.jdbc.DataSourceStub; + +import org.hibernate.testing.FailureExpected; +import org.hibernate.testing.env.ConnectionProviderBuilder; +import org.hibernate.testing.junit4.BaseUnitTestCase; +import org.hibernate.testing.util.jpa.DelegatingPersistenceUnitInfo; +import org.hibernate.testing.util.jpa.PersistenceUnitInfoAdapter; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * @author Steve Ebersole + */ +public class PersistenceUnitOverridesTests extends BaseUnitTestCase { + + @Test + public void testPassingIntegrationJpaJdbcOverrides() { + + // the integration overrides say to use the "db2" JPA connection settings (which should override the persistence unit values) + final Map integrationOverrides = ConnectionProviderBuilder.getJpaConnectionProviderProperties( "db2" ); + + final EntityManagerFactory emf = new HibernatePersistenceProvider().createContainerEntityManagerFactory( + new PersistenceUnitInfoAdapter() { + @Override + public Properties getProperties() { + // effectively, the `persistence.xml` defines `db1` as the connection settings + return ConnectionProviderBuilder.getJpaConnectionProviderProperties( "db1" ); + } + }, + integrationOverrides + ); + + try { + final Map properties = emf.getProperties(); + + final Object hibernateJdbcDriver = properties.get( AvailableSettings.URL ); + assertThat( hibernateJdbcDriver, notNullValue() ); + + final Object jpaJdbcDriver = properties.get( AvailableSettings.JPA_JDBC_URL ); + assertThat( (String) jpaJdbcDriver, containsString( "db2" ) ); + } + finally { + emf.close(); + } + } + + @Test + public void testPassingIntegrationJtaDataSourceOverrideForJpaJdbcSettings() { + final PersistenceUnitInfoAdapter puInfo = new PersistenceUnitInfoAdapter( + ConnectionProviderBuilder.getJpaConnectionProviderProperties( "db2" ) + ); + + final DataSource integrationDataSource = new DataSourceStub( "integrationDataSource" ); + + final HibernatePersistenceProvider provider = new HibernatePersistenceProvider(); + + final EntityManagerFactory emf = provider.createContainerEntityManagerFactory( + puInfo, + Collections.singletonMap( AvailableSettings.JPA_JTA_DATASOURCE, integrationDataSource ) + ); + + // first let's check the DataSource used in the EMF... + final ConnectionProvider connectionProvider = emf.unwrap( SessionFactoryImplementor.class ) + .getServiceRegistry() + .getService( ConnectionProvider.class ); + assertThat( connectionProvider, instanceOf( DatasourceConnectionProviderImpl.class ) ); + final DatasourceConnectionProviderImpl dsCp = (DatasourceConnectionProviderImpl) connectionProvider; + assertThat( dsCp.getDataSource(), is( integrationDataSource ) ); + + // now let's check that it is exposed via the EMF properties + // - note : the spec does not indicate that this should work, but + // it worked this way in previous versions + final Object jtaDs = emf.getProperties().get( AvailableSettings.JPA_JTA_DATASOURCE ); + assertThat( jtaDs, is( integrationDataSource ) ); + + // Additionally, we should have set Hibernate's DATASOURCE setting + final Object hibDs = emf.getProperties().get( AvailableSettings.JPA_JTA_DATASOURCE ); + assertThat( hibDs, is( integrationDataSource ) ); + + // Make sure the non-jta-data-source setting was cleared or otherwise null + final Object nonJtaDs = emf.getProperties().get( AvailableSettings.JPA_NON_JTA_DATASOURCE ); + assertThat( nonJtaDs, nullValue() ); + } + + @Test + public void testPassingIntegrationJpaJdbcOverrideForJtaDataSourceProperty() { + PersistenceProvider provider = new HibernatePersistenceProvider() { + @Override + public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map integrationOverrides) { + return super.createContainerEntityManagerFactory( + new DelegatingPersistenceUnitInfo( info ) { + + // inject a JPA JTA DataSource setting into the PU + final DataSource puDataSource; + final Properties puProperties; + + { + puDataSource = new DataSourceStub( "puDataSource" ); + + puProperties = new Properties(); + puProperties.putAll( info.getProperties() ); + puProperties.put( AvailableSettings.JPA_JTA_DATASOURCE, puDataSource ); + } + + @Override + public DataSource getJtaDataSource() { + return null; + } + + @Override + public DataSource getNonJtaDataSource() { + return null; + } + + @Override + public Properties getProperties() { + return puProperties; + } + }, + integrationOverrides + ); + } + }; + + final EntityManagerFactory emf = provider.createContainerEntityManagerFactory( + new PersistenceUnitInfoAdapter(), + // however, provide JPA connection settings as "integration settings", which according to JPA spec should override the persistence unit values. + // - note that it is unclear in the spec whether JDBC value in the integration settings should override + // a JTA DataSource (nor the reverse). However, that is a useful thing to support + ConnectionProviderBuilder.getJpaConnectionProviderProperties( "db2" ) + ); + + try { + final Map properties = emf.getProperties(); + + final Object hibernateJdbcDriver = properties.get( AvailableSettings.URL ); + assertThat( hibernateJdbcDriver, notNullValue() ); + + final Object jpaJdbcDriver = properties.get( AvailableSettings.JPA_JDBC_URL ); + assertThat( (String) jpaJdbcDriver, containsString( "db2" ) ); + + // see if the values had the affect to adjust the `ConnectionProvider` used + final ConnectionProvider connectionProvider = emf.unwrap( SessionFactoryImplementor.class ) + .getServiceRegistry() + .getService( ConnectionProvider.class ); + assertThat( connectionProvider, instanceOf( DriverManagerConnectionProviderImpl.class ) ); + } + finally { + emf.close(); + } + } + + @Test +// @FailureExpected( +// jiraKey = "HHH-12858", +// message = "Even though the JDBC settings override a DataSource *property*, it" + +// " does not override a DataSource defined using the dedicated persistence.xml element" +// ) + public void testPassingIntegrationJpaJdbcOverridesForJtaDataSourceElement() { + PersistenceProvider provider = new HibernatePersistenceProvider() { + @Override + public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map integrationOverrides) { + return super.createContainerEntityManagerFactory( + new DelegatingPersistenceUnitInfo( info ) { + // inject a JPA JTA DataSource setting into the PU + final DataSource puDataSource = new DataSourceStub( "puDataSource" ); + + @Override + public DataSource getJtaDataSource() { + return puDataSource; + } + }, + integrationOverrides + ); + } + }; + + final EntityManagerFactory emf = provider.createContainerEntityManagerFactory( + new PersistenceUnitInfoAdapter(), + // however, provide JPA connection settings as "integration settings", which according to JPA spec should override the persistence unit values. + // - note that it is unclear in the spec whether JDBC value in the integration settings should override + // a JTA DataSource (nor the reverse). However, that is a useful thing to support + ConnectionProviderBuilder.getJpaConnectionProviderProperties( "db2" ) + ); + + try { + final Map properties = emf.getProperties(); + + final Object hibernateJdbcDriver = properties.get( AvailableSettings.URL ); + assertThat( hibernateJdbcDriver, notNullValue() ); + + final Object jpaJdbcDriver = properties.get( AvailableSettings.JPA_JDBC_URL ); + assertThat( (String) jpaJdbcDriver, containsString( "db2" ) ); + + // see if the values had the affect to adjust the `ConnectionProvider` used + final ConnectionProvider connectionProvider = emf.unwrap( SessionFactoryImplementor.class ) + .getServiceRegistry() + .getService( ConnectionProvider.class ); + assertThat( connectionProvider, instanceOf( DriverManagerConnectionProviderImpl.class ) ); + } + finally { + emf.close(); + } + } + + @Test +// @FailureExpected( +// jiraKey = "HHH-12858", +// message = "So it appears any use of the persistence.xml `jta-data-source` or `non-jta-data-source` " + +// "have precedence over integration settings, which is also incorrect" +// ) + public void testPassingIntegrationJpaDataSourceOverrideForJtaDataSourceElement() { + final DataSource puDataSource = new DataSourceStub( "puDataSource" ); + final DataSource integrationDataSource = new DataSourceStub( "integrationDataSource" ); + + PersistenceProvider provider = new HibernatePersistenceProvider() { + @Override + public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map integrationOverrides) { + return super.createContainerEntityManagerFactory( + new DelegatingPersistenceUnitInfo( info ) { + @Override + public DataSource getJtaDataSource() { + // pretend the DataSource was defined using the `jta-data-source` element in persistence.xml + // - as opposed using `javax.persistence.jtaDataSource` under the `properties` element + return puDataSource; + } + }, + integrationOverrides + ); + } + }; + + final Map integrationOverrides = new HashMap(); + //noinspection unchecked + integrationOverrides.put( AvailableSettings.JPA_JTA_DATASOURCE, integrationDataSource ); + + final EntityManagerFactory emf = provider.createContainerEntityManagerFactory( + new PersistenceUnitInfoAdapter(), + integrationOverrides + ); + + try { + final Map properties = emf.getProperties(); + + final Object datasource = properties.get( AvailableSettings.JPA_JTA_DATASOURCE ); + assertThat( datasource, is( integrationDataSource ) ); + + // see if the values had the affect to adjust the `ConnectionProvider` used + final ConnectionProvider connectionProvider = emf.unwrap( SessionFactoryImplementor.class ) + .getServiceRegistry() + .getService( ConnectionProvider.class ); + assertThat( connectionProvider, instanceOf( DatasourceConnectionProviderImpl.class ) ); + + final DatasourceConnectionProviderImpl datasourceConnectionProvider = (DatasourceConnectionProviderImpl) connectionProvider; + assertThat( datasourceConnectionProvider.getDataSource(), is( integrationDataSource ) ); + } + finally { + emf.close(); + } + } + + @Test + @TestForIssue( jiraKey = "HHH-13640" ) + public void testIntegrationOverridesOfPersistenceXmlDataSource() { + + // mimics a DataSource defined in the persistence.xml + final DataSourceStub dataSource = new DataSourceStub( "puDataSource" ); + final PersistenceUnitInfoAdapter info = new PersistenceUnitInfoAdapter() { + + @Override + public DataSource getNonJtaDataSource() { + return dataSource; + } + }; + + + // Now create "integration Map" that overrides the DataSource to use + final DataSource override = new DataSourceStub( "integrationDataSource" ); + final Map integrationSettings = new HashMap<>(); + integrationSettings.put( AvailableSettings.JPA_NON_JTA_DATASOURCE, override ); + + final PersistenceProvider provider = new HibernatePersistenceProvider(); + + final EntityManagerFactory emf = provider.createContainerEntityManagerFactory( + info, + integrationSettings + ); + + try { + final Map properties = emf.getProperties(); + + assertThat( properties.get( AvailableSettings.JPA_NON_JTA_DATASOURCE ), notNullValue() ); + assertThat( properties.get( AvailableSettings.JPA_NON_JTA_DATASOURCE ), is( override ) ); + + final SessionFactoryImplementor sessionFactory = emf.unwrap( SessionFactoryImplementor.class ); + final ConnectionProvider connectionProvider = sessionFactory.getServiceRegistry().getService( ConnectionProvider.class ); + assertThat( connectionProvider, instanceOf( DatasourceConnectionProviderImpl.class ) ); + + final DatasourceConnectionProviderImpl dsProvider = (DatasourceConnectionProviderImpl) connectionProvider; + assertThat( dsProvider.getDataSource(), is( override ) ); + } + finally { + emf.close(); + } + } + + @Test + @TestForIssue( jiraKey = "HHH-13640" ) + public void testIntegrationOverridesOfPersistenceXmlDataSourceWithDriverManagerInfo() { + + // mimics a DataSource defined in the persistence.xml + final DataSourceStub dataSource = new DataSourceStub( "puDataSource" ); + final PersistenceUnitInfoAdapter info = new PersistenceUnitInfoAdapter() { + + @Override + public DataSource getNonJtaDataSource() { + return dataSource; + } + }; + + final Map integrationSettings = new HashMap<>(); + integrationSettings.put( AvailableSettings.JPA_JDBC_DRIVER, ConnectionProviderBuilder.DRIVER ); + integrationSettings.put( AvailableSettings.JPA_JDBC_URL, ConnectionProviderBuilder.URL ); + integrationSettings.put( AvailableSettings.JPA_JDBC_USER, ConnectionProviderBuilder.USER ); + integrationSettings.put( AvailableSettings.JPA_JDBC_PASSWORD, ConnectionProviderBuilder.PASS ); + + final PersistenceProvider provider = new HibernatePersistenceProvider(); + + final EntityManagerFactory emf = provider.createContainerEntityManagerFactory( + info, + integrationSettings + ); + + final SessionFactoryImplementor sessionFactory = emf.unwrap( SessionFactoryImplementor.class ); + final ConnectionProvider connectionProvider = sessionFactory.getServiceRegistry().getService( ConnectionProvider.class ); + assertThat( connectionProvider, instanceOf( DriverManagerConnectionProviderImpl.class ) ); + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/package-info.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/package-info.java new file mode 100644 index 0000000000..17cbbb7c3c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/package-info.java @@ -0,0 +1,11 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ + +/** + * Tests for Hibernate bootstrapping Hibernate + */ +package org.hibernate.orm.test.bootstrap; diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/env/ConnectionProviderBuilder.java b/hibernate-testing/src/main/java/org/hibernate/testing/env/ConnectionProviderBuilder.java index 980c2446a6..792b30ab6c 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/env/ConnectionProviderBuilder.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/env/ConnectionProviderBuilder.java @@ -6,25 +6,15 @@ */ package org.hibernate.testing.env; -import java.io.PrintWriter; import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.SQLException; -import java.sql.SQLFeatureNotSupportedException; import java.util.Collections; -import java.util.HashMap; -import java.util.Map; import java.util.Properties; -import java.util.logging.Logger; - import javax.sql.DataSource; -import javassist.scopedpool.SoftValueHashMap; - -import org.hibernate.annotations.common.reflection.ReflectionUtil; import org.hibernate.cfg.Environment; import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl; @@ -38,27 +28,42 @@ import org.hibernate.testing.DialectCheck; * * @author Steve Ebersole */ +@SuppressWarnings({"WeakerAccess", "unused"}) public class ConnectionProviderBuilder implements DialectCheck { public static final String DRIVER = "org.h2.Driver"; public static final String DATA_SOURCE = "org.h2.jdbcx.JdbcDataSource"; // public static final String URL = "jdbc:h2:mem:%s;DB_CLOSE_DELAY=-1;MVCC=TRUE"; - public static final String URL = "jdbc:h2:mem:%s;DB_CLOSE_DELAY=-1"; + public static final String URL_FORMAT = "jdbc:h2:mem:%s;DB_CLOSE_DELAY=-1"; + public static final String URL = URL_FORMAT; public static final String USER = "sa"; public static final String PASS = ""; public static Properties getConnectionProviderProperties(String dbName) { Properties props = new Properties( null ); props.put( Environment.DRIVER, DRIVER ); - props.put( Environment.URL, String.format( URL, dbName ) ); + props.put( Environment.URL, String.format( URL_FORMAT, dbName ) ); props.put( Environment.USER, USER ); props.put( Environment.PASS, PASS ); return props; } + public static Properties getJpaConnectionProviderProperties(String dbName) { + Properties props = new Properties( null ); + props.put( Environment.JPA_JDBC_DRIVER, DRIVER ); + props.put( Environment.JPA_JDBC_URL, String.format( URL_FORMAT, dbName ) ); + props.put( Environment.JPA_JDBC_USER, USER ); + props.put( Environment.JPA_JDBC_PASSWORD, PASS ); + return props; + } + public static Properties getConnectionProviderProperties() { return getConnectionProviderProperties( "db1" ); } + public static Properties getJpaConnectionProviderProperties() { + return getJpaConnectionProviderProperties( "db1" ); + } + public static DriverManagerConnectionProviderImpl buildConnectionProvider() { return buildConnectionProvider( false ); } @@ -133,7 +138,7 @@ public class ConnectionProviderBuilder implements DialectCheck { return connectionProxy; } - private class ConnectionInvocationHandler implements InvocationHandler { + private static class ConnectionInvocationHandler implements InvocationHandler { private final Connection target; diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/DataSourceStub.java b/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/DataSourceStub.java new file mode 100644 index 0000000000..cb1866a3ad --- /dev/null +++ b/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/DataSourceStub.java @@ -0,0 +1,88 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.testing.jdbc; + +import java.io.PrintWriter; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.logging.Logger; +import javax.sql.DataSource; + +import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; + +import org.hibernate.testing.env.ConnectionProviderBuilder; + +/** + * @author Steve Ebersole + */ +public class DataSourceStub implements DataSource { + private final String id; + private final DriverManagerConnectionProviderImpl connectionProvider; + private PrintWriter printWriter; + + public DataSourceStub(String id) { + this.id = id; + connectionProvider = new DriverManagerConnectionProviderImpl(); + connectionProvider.configure( ConnectionProviderBuilder.getConnectionProviderProperties() ); + + printWriter = null; + } + + public String getId() { + return id; + } + + @Override + public Connection getConnection() throws SQLException { + return connectionProvider.getConnection(); + } + + @Override + public Connection getConnection(String username, String password) { + throw new UnsupportedOperationException(); + } + + @Override + public PrintWriter getLogWriter() { + return printWriter; + } + + @Override + public void setLogWriter(PrintWriter out) { + this.printWriter = out; + } + + @Override + public void setLoginTimeout(int seconds) { + } + + @Override + public int getLoginTimeout() { + return -1; + } + + @Override + public Logger getParentLogger() { + return Logger.getGlobal(); + } + + @Override + public T unwrap(Class iface) { + //noinspection unchecked + return (T) this; + } + + @Override + public boolean isWrapperFor(Class iface) { + return iface.isAssignableFrom( getClass() ); + } + + @Override + public String toString() { + return "DataSourceImpl(" + id + ")"; + } +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/util/jpa/DelegatingPersistenceUnitInfo.java b/hibernate-testing/src/main/java/org/hibernate/testing/util/jpa/DelegatingPersistenceUnitInfo.java new file mode 100644 index 0000000000..dcb44b942b --- /dev/null +++ b/hibernate-testing/src/main/java/org/hibernate/testing/util/jpa/DelegatingPersistenceUnitInfo.java @@ -0,0 +1,110 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.testing.util.jpa; + +import javax.persistence.SharedCacheMode; +import javax.persistence.ValidationMode; +import javax.persistence.spi.ClassTransformer; +import javax.persistence.spi.PersistenceUnitInfo; +import javax.persistence.spi.PersistenceUnitTransactionType; +import javax.sql.DataSource; +import java.net.URL; +import java.util.List; +import java.util.Properties; + +public class DelegatingPersistenceUnitInfo implements PersistenceUnitInfo { + private final PersistenceUnitInfo delegate; + + public DelegatingPersistenceUnitInfo(PersistenceUnitInfo delegate) { + this.delegate = delegate; + } + + @Override + public String getPersistenceUnitName() { + return delegate.getPersistenceUnitName(); + } + + @Override + public String getPersistenceProviderClassName() { + return delegate.getPersistenceProviderClassName(); + } + + @Override + public PersistenceUnitTransactionType getTransactionType() { + return delegate.getTransactionType(); + } + + @Override + public DataSource getJtaDataSource() { + return delegate.getJtaDataSource(); + } + + @Override + public DataSource getNonJtaDataSource() { + return delegate.getNonJtaDataSource(); + } + + @Override + public List getMappingFileNames() { + return delegate.getMappingFileNames(); + } + + @Override + public List getJarFileUrls() { + return delegate.getJarFileUrls(); + } + + @Override + public URL getPersistenceUnitRootUrl() { + return delegate.getPersistenceUnitRootUrl(); + } + + @Override + public List getManagedClassNames() { + return delegate.getManagedClassNames(); + } + + @Override + public boolean excludeUnlistedClasses() { + return delegate.excludeUnlistedClasses(); + } + + @Override + public SharedCacheMode getSharedCacheMode() { + return delegate.getSharedCacheMode(); + } + + @Override + public ValidationMode getValidationMode() { + return delegate.getValidationMode(); + } + + @Override + public Properties getProperties() { + return delegate.getProperties(); + } + + @Override + public String getPersistenceXMLSchemaVersion() { + return delegate.getPersistenceXMLSchemaVersion(); + } + + @Override + public ClassLoader getClassLoader() { + return delegate.getClassLoader(); + } + + @Override + public void addTransformer(ClassTransformer transformer) { + delegate.addTransformer( transformer ); + } + + @Override + public ClassLoader getNewTempClassLoader() { + return null; + } +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/util/jpa/PersistenceUnitInfoAdapter.java b/hibernate-testing/src/main/java/org/hibernate/testing/util/jpa/PersistenceUnitInfoAdapter.java new file mode 100644 index 0000000000..00d29ffcaa --- /dev/null +++ b/hibernate-testing/src/main/java/org/hibernate/testing/util/jpa/PersistenceUnitInfoAdapter.java @@ -0,0 +1,117 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.testing.util.jpa; + +import javax.persistence.SharedCacheMode; +import javax.persistence.ValidationMode; +import javax.persistence.spi.ClassTransformer; +import javax.persistence.spi.PersistenceUnitInfo; +import javax.persistence.spi.PersistenceUnitTransactionType; +import javax.sql.DataSource; +import java.net.URL; +import java.util.Collections; +import java.util.List; +import java.util.Properties; + +import org.hibernate.jpa.HibernatePersistenceProvider; + +public class PersistenceUnitInfoAdapter implements PersistenceUnitInfo { + private final Properties properties; + + public PersistenceUnitInfoAdapter() { + this( new Properties() ); + } + + public PersistenceUnitInfoAdapter(Properties properties) { + this.properties = properties; + } + + @Override + public String getPersistenceUnitName() { + return "pu"; + } + + @Override + public String getPersistenceProviderClassName() { + return HibernatePersistenceProvider.class.getName(); + } + + @Override + public PersistenceUnitTransactionType getTransactionType() { + return PersistenceUnitTransactionType.RESOURCE_LOCAL; + } + + @Override + public DataSource getJtaDataSource() { + return null; + } + + @Override + public DataSource getNonJtaDataSource() { + return null; + } + + @Override + public List getMappingFileNames() { + return Collections.emptyList(); + } + + @Override + public List getJarFileUrls() { + return Collections.emptyList(); + } + + @Override + public URL getPersistenceUnitRootUrl() { + return null; + } + + @Override + public List getManagedClassNames() { + return Collections.emptyList(); + } + + @Override + public boolean excludeUnlistedClasses() { + return false; + } + + @Override + public SharedCacheMode getSharedCacheMode() { + return SharedCacheMode.NONE; + } + + @Override + public ValidationMode getValidationMode() { + return ValidationMode.NONE; + } + + @Override + public Properties getProperties() { + return properties; + } + + @Override + public String getPersistenceXMLSchemaVersion() { + return "2.1"; + } + + @Override + public ClassLoader getClassLoader() { + return PersistenceUnitInfoAdapter.class.getClassLoader(); + } + + @Override + public void addTransformer(ClassTransformer transformer) { + throw new UnsupportedOperationException(); + } + + @Override + public ClassLoader getNewTempClassLoader() { + return null; + } +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/util/jpa/PersistenceUnitInfoPropertiesWrapper.java b/hibernate-testing/src/main/java/org/hibernate/testing/util/jpa/PersistenceUnitInfoPropertiesWrapper.java new file mode 100644 index 0000000000..abfa5d7023 --- /dev/null +++ b/hibernate-testing/src/main/java/org/hibernate/testing/util/jpa/PersistenceUnitInfoPropertiesWrapper.java @@ -0,0 +1,130 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ + +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.testing.util.jpa; + +import java.net.URL; +import java.util.Collections; +import java.util.List; +import java.util.Properties; +import javax.persistence.SharedCacheMode; +import javax.persistence.ValidationMode; +import javax.persistence.spi.ClassTransformer; +import javax.persistence.spi.PersistenceUnitInfo; +import javax.persistence.spi.PersistenceUnitTransactionType; +import javax.sql.DataSource; + +import org.hibernate.jpa.HibernatePersistenceProvider; + +/** + * TODO : javadoc + * + * @author Steve Ebersole + */ +public class PersistenceUnitInfoPropertiesWrapper implements PersistenceUnitInfo { + private Properties properties; + + public PersistenceUnitInfoPropertiesWrapper() { + } + + public PersistenceUnitInfoPropertiesWrapper(Properties properties) { + this.properties = properties; + } + + public String getPersistenceUnitName() { + return "persistenceUnitAdapter"; + } + + public String getPersistenceProviderClassName() { + return HibernatePersistenceProvider.class.getName(); + } + + public PersistenceUnitTransactionType getTransactionType() { + return null; + } + + public DataSource getJtaDataSource() { + return null; + } + + public DataSource getNonJtaDataSource() { + return null; + } + + public List getMappingFileNames() { + return Collections.emptyList(); + } + + public List getJarFileUrls() { + return Collections.emptyList(); + } + + public URL getPersistenceUnitRootUrl() { + return null; + } + + public List getManagedClassNames() { + return Collections.emptyList(); + } + + public boolean excludeUnlistedClasses() { + return false; + } + + public SharedCacheMode getSharedCacheMode() { + return null; + } + + public ValidationMode getValidationMode() { + return null; + } + + public Properties getProperties() { + if ( properties == null ) { + properties = new Properties(); + } + return properties; + } + + public String getPersistenceXMLSchemaVersion() { + return null; + } + + public ClassLoader getClassLoader() { + return Thread.currentThread().getContextClassLoader(); + } + + public void addTransformer(ClassTransformer transformer) { + } + + public ClassLoader getNewTempClassLoader() { + return Thread.currentThread().getContextClassLoader(); + } +} diff --git a/tooling/hibernate-gradle-plugin/src/main/groovy/org/hibernate/orm/tooling/gradle/HibernatePlugin.java b/tooling/hibernate-gradle-plugin/src/main/groovy/org/hibernate/orm/tooling/gradle/HibernatePlugin.java index 77e19072ca..48ec320c12 100644 --- a/tooling/hibernate-gradle-plugin/src/main/groovy/org/hibernate/orm/tooling/gradle/HibernatePlugin.java +++ b/tooling/hibernate-gradle-plugin/src/main/groovy/org/hibernate/orm/tooling/gradle/HibernatePlugin.java @@ -6,36 +6,11 @@ */ package org.hibernate.orm.tooling.gradle; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.List; - -import org.gradle.api.Action; -import org.gradle.api.GradleException; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.Task; -import org.gradle.api.file.FileCollection; -import org.gradle.api.file.FileTree; -import org.gradle.api.logging.Logger; -import org.gradle.api.logging.Logging; import org.gradle.api.tasks.SourceSet; -import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext; -import org.hibernate.bytecode.enhance.spi.EnhancementContext; -import org.hibernate.bytecode.enhance.spi.Enhancer; -import org.hibernate.bytecode.enhance.spi.UnloadedClass; -import org.hibernate.bytecode.enhance.spi.UnloadedField; -import org.hibernate.cfg.Environment; - /** * The Hibernate Gradle plugin. Adds Hibernate build-time capabilities into your Gradle-based build. * @@ -44,8 +19,6 @@ import org.hibernate.cfg.Environment; */ @SuppressWarnings("serial") public class HibernatePlugin implements Plugin { - private final Logger logger = Logging.getLogger( HibernatePlugin.class ); - public void apply(Project project) { project.getPlugins().apply( "java" ); @@ -55,16 +28,12 @@ public class HibernatePlugin implements Plugin { project.getExtensions().add( "hibernate", hibernateExtension ); project.afterEvaluate( - p -> { - if ( hibernateExtension.enhance != null ) { - applyEnhancement( p, hibernateExtension ); - } - } + p -> applyEnhancement( p, hibernateExtension ) ); } private void applyEnhancement(final Project project, final HibernateExtension hibernateExtension) { - if ( !hibernateExtension.enhance.shouldApply() ) { + if ( hibernateExtension.enhance == null || ! hibernateExtension.enhance.shouldApply() ) { project.getLogger().warn( "Skipping Hibernate bytecode enhancement since no feature is enabled" ); return; }