package org.hibernate.jpa.boot.internal;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import javax.persistence.AttributeConverter;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityNotFoundException;
import javax.persistence.PersistenceException;
import javax.persistence.spi.PersistenceUnitTransactionType;
import javax.sql.DataSource;
import org.hibernate.Interceptor;
import org.hibernate.InvalidMappingException;
import org.hibernate.MappingException;
import org.hibernate.MappingNotFoundException;
import org.hibernate.SessionFactory;
import org.hibernate.SessionFactoryObserver;
import org.hibernate.boot.registry.BootstrapServiceRegistry;
import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.registry.selector.StrategyRegistrationProvider;
import org.hibernate.boot.registry.selector.spi.StrategySelector;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.cfg.NamingStrategy;
import org.hibernate.cfg.beanvalidation.BeanValidationIntegrator;
import org.hibernate.cfg.naming.NamingStrategyDelegator;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory;
import org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory;
import org.hibernate.id.factory.spi.MutableIdentifierGeneratorFactory;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.internal.jaxb.cfg.JaxbHibernateConfiguration;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.ValueHolder;
import org.hibernate.jpa.AvailableSettings;
import org.hibernate.jpa.boot.scan.internal.StandardScanOptions;
import org.hibernate.jpa.boot.scan.internal.StandardScanner;
import org.hibernate.jpa.boot.scan.spi.ScanOptions;
import org.hibernate.jpa.boot.scan.spi.ScanResult;
import org.hibernate.jpa.boot.scan.spi.Scanner;
import org.hibernate.jpa.boot.spi.ClassDescriptor;
import org.hibernate.jpa.boot.spi.EntityManagerFactoryBuilder;
import org.hibernate.jpa.boot.spi.InputStreamAccess;
import org.hibernate.jpa.boot.spi.IntegratorProvider;
import org.hibernate.jpa.boot.spi.MappingFileDescriptor;
import org.hibernate.jpa.boot.spi.NamedInputStream;
import org.hibernate.jpa.boot.spi.PackageDescriptor;
import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor;
import org.hibernate.jpa.boot.spi.StrategyRegistrationProviderList;
import org.hibernate.jpa.boot.spi.TypeContributorList;
import org.hibernate.jpa.event.spi.JpaIntegrator;
import org.hibernate.jpa.internal.EntityManagerFactoryImpl;
import org.hibernate.jpa.internal.EntityManagerMessageLogger;
import org.hibernate.jpa.internal.schemagen.JpaSchemaGenerator;
import org.hibernate.jpa.internal.util.LogHelper;
import org.hibernate.jpa.internal.util.PersistenceUnitTransactionTypeHelper;
import org.hibernate.jpa.spi.IdentifierGeneratorStrategyProvider;
import org.hibernate.metamodel.source.annotations.JPADotNames;
import org.hibernate.metamodel.source.annotations.JandexHelper;
import org.hibernate.metamodel.spi.TypeContributor;
import org.hibernate.proxy.EntityNotFoundDelegate;
import org.hibernate.secure.spi.GrantedPermission;
import org.hibernate.secure.spi.JaccService;
import org.hibernate.service.ConfigLoader;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.Index;
import org.jboss.jandex.IndexView;
import org.jboss.jandex.Indexer;
import org.jboss.logging.Logger;
public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuilder {
private static final EntityManagerMessageLogger LOG = Logger.getMessageLogger(
EntityManagerMessageLogger.class,
EntityManagerFactoryBuilderImpl.class.getName()
);
private static final String META_INF_ORM_XML = "META-INF/orm.xml";
public static final String INTEGRATOR_PROVIDER = "hibernate.integrator_provider";
public static final String STRATEGY_REGISTRATION_PROVIDERS = "hibernate.strategy_registration_provider";
public static final String TYPE_CONTRIBUTORS = "hibernate.type_contributors";
public static final String JANDEX_INDEX = "hibernate.jandex_index";
private Object validatorFactory;
private DataSource dataSource;
private final PersistenceUnitDescriptor persistenceUnit;
private final SettingsImpl settings = new SettingsImpl();
private final StandardServiceRegistryBuilder serviceRegistryBuilder;
private final Map configurationValues;
private final List<GrantedPermission> grantedJaccPermissions = new ArrayList<GrantedPermission>();
private final List<CacheRegionDefinition> cacheRegionDefinitions = new ArrayList<CacheRegionDefinition>();
private final List<JaxbHibernateConfiguration.JaxbSessionFactory.JaxbMapping> cfgXmlNamedMappings = new ArrayList<JaxbHibernateConfiguration.JaxbSessionFactory.JaxbMapping>();
private Interceptor sessionFactoryInterceptor;
private NamingStrategy namingStrategy;
private NamingStrategyDelegator namingStrategyDelegator;
private SessionFactoryObserver suppliedSessionFactoryObserver;
private MetadataSources metadataSources;
private Configuration hibernateConfiguration;
private static EntityNotFoundDelegate jpaEntityNotFoundDelegate = new JpaEntityNotFoundDelegate();
private ClassLoader providedClassLoader;
private static class JpaEntityNotFoundDelegate implements EntityNotFoundDelegate, Serializable {
public void handleEntityNotFound(String entityName, Serializable id) {
throw new EntityNotFoundException( "Unable to find " + entityName + " with id " + id );
}
}
public EntityManagerFactoryBuilderImpl(PersistenceUnitDescriptor persistenceUnit, Map integrationSettings) {
this( persistenceUnit, integrationSettings, null );
}
public EntityManagerFactoryBuilderImpl(
PersistenceUnitDescriptor persistenceUnit,
Map integrationSettings,
ClassLoader providedClassLoader ) {
LogHelper.logPersistenceUnitInformation( persistenceUnit );
this.persistenceUnit = persistenceUnit;
if ( integrationSettings == null ) {
integrationSettings = Collections.emptyMap();
}
this.providedClassLoader = providedClassLoader;
final BootstrapServiceRegistry bootstrapServiceRegistry = buildBootstrapServiceRegistry( integrationSettings );
this.serviceRegistryBuilder = new StandardServiceRegistryBuilder( bootstrapServiceRegistry );
this.configurationValues = mergePropertySources( persistenceUnit, integrationSettings, bootstrapServiceRegistry );
this.serviceRegistryBuilder.applySettings( configurationValues );
final ScanResult scanResult = scan( bootstrapServiceRegistry );
final DeploymentResources deploymentResources = buildDeploymentResources( scanResult, bootstrapServiceRegistry );
final IndexView jandexIndex = locateOrBuildJandexIndex( deploymentResources );
metadataSources = prepareMetadataSources( jandexIndex, deploymentResources, bootstrapServiceRegistry );
withValidatorFactory( configurationValues.get( AvailableSettings.VALIDATION_FACTORY ) );
final boolean useClassTransformer = "true".equals( configurationValues.remove( AvailableSettings.USE_CLASS_ENHANCER ) );
if ( useClassTransformer ) {
persistenceUnit.pushClassTransformer( metadataSources.collectMappingClassNames() );
}
}
private static interface DeploymentResources {
public Iterable<ClassDescriptor> getClassDescriptors();
public Iterable<PackageDescriptor> getPackageDescriptors();
public Iterable<MappingFileDescriptor> getMappingFileDescriptors();
}
private DeploymentResources buildDeploymentResources(
ScanResult scanResult,
BootstrapServiceRegistry bootstrapServiceRegistry) {
final ArrayList<MappingFileDescriptor> mappingFileDescriptors = new ArrayList<MappingFileDescriptor>();
final Set<String> nonLocatedMappingFileNames = new HashSet<String>();
final List<String> explicitMappingFileNames = persistenceUnit.getMappingFileNames();
if ( explicitMappingFileNames != null ) {
nonLocatedMappingFileNames.addAll( explicitMappingFileNames );
}
for ( MappingFileDescriptor mappingFileDescriptor : scanResult.getLocatedMappingFiles() ) {
mappingFileDescriptors.add( mappingFileDescriptor );
nonLocatedMappingFileNames.remove( mappingFileDescriptor.getName() );
}
for ( String name : nonLocatedMappingFileNames ) {
MappingFileDescriptor descriptor = buildMappingFileDescriptor( name, bootstrapServiceRegistry );
mappingFileDescriptors.add( descriptor );
}
final HashMap<String, ClassDescriptor> classDescriptorMap = new HashMap<String, ClassDescriptor>();
final HashMap<String, PackageDescriptor> packageDescriptorMap = new HashMap<String, PackageDescriptor>();
for ( ClassDescriptor classDescriptor : scanResult.getLocatedClasses() ) {
classDescriptorMap.put( classDescriptor.getName(), classDescriptor );
}
for ( PackageDescriptor packageDescriptor : scanResult.getLocatedPackages() ) {
packageDescriptorMap.put( packageDescriptor.getName(), packageDescriptor );
}
final List<String> explicitClassNames = persistenceUnit.getManagedClassNames();
if ( explicitClassNames != null ) {
for ( String explicitClassName : explicitClassNames ) {
if ( classDescriptorMap.containsKey( explicitClassName ) ) {
continue;
}
if ( packageDescriptorMap.containsKey( explicitClassName ) ) {
continue;
}
final String classFileName = explicitClassName.replace( '.', '/' ) + ".class";
final URL classFileUrl = bootstrapServiceRegistry.getService( ClassLoaderService.class )
.locateResource( classFileName );
if ( classFileUrl != null ) {
classDescriptorMap.put(
explicitClassName,
new ClassDescriptorImpl( explicitClassName, new UrlInputStreamAccess( classFileUrl ) )
);
continue;
}
final String packageInfoFileName = explicitClassName.replace( '.', '/' ) + "/package-info.class";
final URL packageInfoFileUrl = bootstrapServiceRegistry.getService( ClassLoaderService.class )
.locateResource( packageInfoFileName );
if ( packageInfoFileUrl != null ) {
packageDescriptorMap.put(
explicitClassName,
new PackageDescriptorImpl( explicitClassName, new UrlInputStreamAccess( packageInfoFileUrl ) )
);
continue;
}
LOG.debugf(
"Unable to resolve class [%s] named in persistence unit [%s]",
explicitClassName,
persistenceUnit.getName()
);
}
}
return new DeploymentResources() {
@Override
public Iterable<ClassDescriptor> getClassDescriptors() {
return classDescriptorMap.values();
}
@Override
public Iterable<PackageDescriptor> getPackageDescriptors() {
return packageDescriptorMap.values();
}
@Override
public Iterable<MappingFileDescriptor> getMappingFileDescriptors() {
return mappingFileDescriptors;
}
};
}
private MappingFileDescriptor buildMappingFileDescriptor(
String name,
BootstrapServiceRegistry bootstrapServiceRegistry) {
final URL url = bootstrapServiceRegistry.getService( ClassLoaderService.class ).locateResource( name );
if ( url == null ) {
throw persistenceException( "Unable to resolve named mapping-file [" + name + "]" );
}
return new MappingFileDescriptorImpl( name, new UrlInputStreamAccess( url ) );
}
@SuppressWarnings("unchecked")
public Map getConfigurationValues() {
return Collections.unmodifiableMap( configurationValues );
}
public Configuration getHibernateConfiguration() {
return hibernateConfiguration;
}
@SuppressWarnings("unchecked")
private MetadataSources prepareMetadataSources(
IndexView jandexIndex,
DeploymentResources deploymentResources,
BootstrapServiceRegistry bootstrapServiceRegistry) {
MetadataSources metadataSources = new MetadataSources();
for ( ClassDescriptor classDescriptor : deploymentResources.getClassDescriptors() ) {
final String className = classDescriptor.getName();
final ClassInfo classInfo = jandexIndex.getClassByName( DotName.createSimple( className ) );
if ( classInfo == null ) {
metadataSources.annotatedMappingClassNames.add( className );
continue;
}
AnnotationInstance converterAnnotation = JandexHelper.getSingleAnnotation(
classInfo.annotations(),
JPADotNames.CONVERTER
);
if ( converterAnnotation != null ) {
metadataSources.converterDescriptors.add(
new MetadataSources.ConverterDescriptor(
className,
JandexHelper.getValue( converterAnnotation, "autoApply", boolean.class,
bootstrapServiceRegistry.getService( ClassLoaderService.class ) )
)
);
}
else {
metadataSources.annotatedMappingClassNames.add( className );
}
}
for ( PackageDescriptor packageDescriptor : deploymentResources.getPackageDescriptors() ) {
metadataSources.packageNames.add( packageDescriptor.getName() );
}
for ( MappingFileDescriptor mappingFileDescriptor : deploymentResources.getMappingFileDescriptors() ) {
metadataSources.namedMappingFileInputStreams.add( mappingFileDescriptor.getStreamAccess().asNamedInputStream() );
}
final String explicitHbmXmls = (String) configurationValues.remove( AvailableSettings.HBXML_FILES );
if ( explicitHbmXmls != null ) {
metadataSources.mappingFileResources.addAll( Arrays.asList( StringHelper.split( ", ", explicitHbmXmls ) ) );
}
final List<String> explicitOrmXml = (List<String>) configurationValues.remove( AvailableSettings.XML_FILE_NAMES );
if ( explicitOrmXml != null ) {
metadataSources.mappingFileResources.addAll( explicitOrmXml );
}
return metadataSources;
}
private IndexView locateOrBuildJandexIndex(DeploymentResources deploymentResources) {
IndexView jandexIndex = (IndexView) configurationValues.get( JANDEX_INDEX );
if ( jandexIndex == null ) {
jandexIndex = buildJandexIndex( deploymentResources );
}
return jandexIndex;
}
private IndexView buildJandexIndex(DeploymentResources deploymentResources) {
Indexer indexer = new Indexer();
for ( ClassDescriptor classDescriptor : deploymentResources.getClassDescriptors() ) {
indexStream( indexer, classDescriptor.getStreamAccess() );
}
for ( PackageDescriptor packageDescriptor : deploymentResources.getPackageDescriptors() ) {
indexStream( indexer, packageDescriptor.getStreamAccess() );
}
return indexer.complete();
}
private void indexStream(Indexer indexer, InputStreamAccess streamAccess) {
try {
InputStream stream = streamAccess.accessInputStream();
try {
indexer.index( stream );
}
finally {
try {
stream.close();
}
catch (Exception ignore) {
}
}
}
catch ( IOException e ) {
throw persistenceException( "Unable to index from stream " + streamAccess.getStreamName(), e );
}
}
private BootstrapServiceRegistry buildBootstrapServiceRegistry(Map integrationSettings) {
final BootstrapServiceRegistryBuilder bootstrapServiceRegistryBuilder = new BootstrapServiceRegistryBuilder();
bootstrapServiceRegistryBuilder.with( new JpaIntegrator() );
final IntegratorProvider integratorProvider = (IntegratorProvider) integrationSettings.get( INTEGRATOR_PROVIDER );
if ( integratorProvider != null ) {
for ( Integrator integrator : integratorProvider.getIntegrators() ) {
bootstrapServiceRegistryBuilder.with( integrator );
}
}
final StrategyRegistrationProviderList strategyRegistrationProviderList
= (StrategyRegistrationProviderList) integrationSettings.get( STRATEGY_REGISTRATION_PROVIDERS );
if ( strategyRegistrationProviderList != null ) {
for ( StrategyRegistrationProvider strategyRegistrationProvider : strategyRegistrationProviderList
.getStrategyRegistrationProviders() ) {
bootstrapServiceRegistryBuilder.withStrategySelectors( strategyRegistrationProvider );
}
}
ClassLoader classLoader;
ClassLoader appClassLoader = (ClassLoader) integrationSettings.get( org.hibernate.cfg.AvailableSettings.APP_CLASSLOADER );
if ( providedClassLoader != null ) {
classLoader = providedClassLoader;
}
else if ( appClassLoader != null ) {
classLoader = appClassLoader;
}
else {
classLoader = persistenceUnit.getClassLoader();
}
bootstrapServiceRegistryBuilder.with( classLoader );
return bootstrapServiceRegistryBuilder.build();
}
@SuppressWarnings("unchecked")
private Map mergePropertySources(
PersistenceUnitDescriptor persistenceUnit,
Map integrationSettings,
final BootstrapServiceRegistry bootstrapServiceRegistry) {
final Map merged = new HashMap();
if ( persistenceUnit.getProperties() != null ) {
merged.putAll( persistenceUnit.getProperties() );
}
merged.put( AvailableSettings.PERSISTENCE_UNIT_NAME, persistenceUnit.getName() );
final ValueHolder<ConfigLoader> configLoaderHolder = new ValueHolder<ConfigLoader>(
new ValueHolder.DeferredInitializer<ConfigLoader>() {
@Override
public ConfigLoader initialize() {
return new ConfigLoader( bootstrapServiceRegistry );
}
}
);
final String cfgXmlResourceName1 = (String) merged.remove( AvailableSettings.CFG_FILE );
if ( StringHelper.isNotEmpty( cfgXmlResourceName1 ) ) {
JaxbHibernateConfiguration configurationElement = configLoaderHolder.getValue()
.loadConfigXmlResource( cfgXmlResourceName1 );
processHibernateConfigurationElement( configurationElement, merged );
}
final String cfgXmlResourceName2 = (String) integrationSettings.get( AvailableSettings.CFG_FILE );
if ( StringHelper.isNotEmpty( cfgXmlResourceName2 ) ) {
integrationSettings.remove( AvailableSettings.CFG_FILE );
JaxbHibernateConfiguration configurationElement = configLoaderHolder.getValue().loadConfigXmlResource(
cfgXmlResourceName2
);
processHibernateConfigurationElement( configurationElement, merged );
}
merged.putAll( integrationSettings );
if ( !merged.containsKey( AvailableSettings.VALIDATION_MODE ) ) {
if ( persistenceUnit.getValidationMode() != null ) {
merged.put( AvailableSettings.VALIDATION_MODE, persistenceUnit.getValidationMode() );
}
}
if ( !merged.containsKey( AvailableSettings.SHARED_CACHE_MODE ) ) {
if ( persistenceUnit.getSharedCacheMode() != null ) {
merged.put( AvailableSettings.SHARED_CACHE_MODE, persistenceUnit.getSharedCacheMode() );
}
}
Iterator itr = merged.entrySet().iterator();
while ( itr.hasNext() ) {
final Map.Entry entry = (Map.Entry) itr.next();
if ( entry.getValue() == null ) {
itr.remove();
}
}
return merged;
}
@SuppressWarnings("unchecked")
private void processHibernateConfigurationElement(
JaxbHibernateConfiguration configurationElement,
Map mergeMap) {
if ( ! mergeMap.containsKey( org.hibernate.cfg.AvailableSettings.SESSION_FACTORY_NAME ) ) {
String cfgName = configurationElement.getSessionFactory().getName();
if ( cfgName != null ) {
mergeMap.put( org.hibernate.cfg.AvailableSettings.SESSION_FACTORY_NAME, cfgName );
}
}
for ( JaxbHibernateConfiguration.JaxbSessionFactory.JaxbProperty jaxbProperty : configurationElement.getSessionFactory().getProperty() ) {
mergeMap.put( jaxbProperty.getName(), jaxbProperty.getValue() );
}
for ( JaxbHibernateConfiguration.JaxbSessionFactory.JaxbMapping jaxbMapping : configurationElement.getSessionFactory().getMapping() ) {
cfgXmlNamedMappings.add( jaxbMapping );
}
for ( Object cacheDeclaration : configurationElement.getSessionFactory().getClassCacheOrCollectionCache() ) {
if ( JaxbHibernateConfiguration.JaxbSessionFactory.JaxbClassCache.class.isInstance( cacheDeclaration ) ) {
final JaxbHibernateConfiguration.JaxbSessionFactory.JaxbClassCache jaxbClassCache
= (JaxbHibernateConfiguration.JaxbSessionFactory.JaxbClassCache) cacheDeclaration;
cacheRegionDefinitions.add(
new CacheRegionDefinition(
CacheRegionDefinition.CacheType.ENTITY,
jaxbClassCache.getClazz(),
jaxbClassCache.getUsage().value(),
jaxbClassCache.getRegion(),
"all".equals( jaxbClassCache.getInclude() )
)
);
}
else {
final JaxbHibernateConfiguration.JaxbSessionFactory.JaxbCollectionCache jaxbCollectionCache
= (JaxbHibernateConfiguration.JaxbSessionFactory.JaxbCollectionCache) cacheDeclaration;
cacheRegionDefinitions.add(
new CacheRegionDefinition(
CacheRegionDefinition.CacheType.COLLECTION,
jaxbCollectionCache.getCollection(),
jaxbCollectionCache.getUsage().value(),
jaxbCollectionCache.getRegion(),
false
)
);
}
}
if ( configurationElement.getSecurity() != null ) {
for ( JaxbHibernateConfiguration.JaxbSecurity.JaxbGrant grant : configurationElement.getSecurity().getGrant() ) {
grantedJaccPermissions.add(
new GrantedPermission(
grant.getRole(),
grant.getEntityName(),
grant.getActions()
)
);
}
}
}
private String jaccContextId;
private void addJaccDefinition(String key, Object value) {
if ( jaccContextId == null ) {
jaccContextId = (String) configurationValues.get( AvailableSettings.JACC_CONTEXT_ID );
if ( jaccContextId == null ) {
throw persistenceException(
"Entities have been configured for JACC, but "
+ AvailableSettings.JACC_CONTEXT_ID + " has not been set"
);
}
}
try {
final int roleStart = AvailableSettings.JACC_PREFIX.length() + 1;
final String role = key.substring( roleStart, key.indexOf( '.', roleStart ) );
final int classStart = roleStart + role.length() + 1;
final String clazz = key.substring( classStart, key.length() );
grantedJaccPermissions.add( new GrantedPermission( role, clazz, (String) value ) );
}
catch ( IndexOutOfBoundsException e ) {
throw persistenceException( "Illegal usage of " + AvailableSettings.JACC_PREFIX + ": " + key );
}
}
private void addCacheRegionDefinition(String role, String value, CacheRegionDefinition.CacheType cacheType) {
final StringTokenizer params = new StringTokenizer( value, ";, " );
if ( !params.hasMoreTokens() ) {
StringBuilder error = new StringBuilder( "Illegal usage of " );
if ( cacheType == CacheRegionDefinition.CacheType.ENTITY ) {
error.append( AvailableSettings.CLASS_CACHE_PREFIX )
.append( ": " )
.append( AvailableSettings.CLASS_CACHE_PREFIX );
}
else {
error.append( AvailableSettings.COLLECTION_CACHE_PREFIX )
.append( ": " )
.append( AvailableSettings.COLLECTION_CACHE_PREFIX );
}
error.append( '.' )
.append( role )
.append( ' ' )
.append( value )
.append( ". Was expecting configuration, but found none" );
throw persistenceException( error.toString() );
}
String usage = params.nextToken();
String region = null;
if ( params.hasMoreTokens() ) {
region = params.nextToken();
}
boolean lazyProperty = true;
if ( cacheType == CacheRegionDefinition.CacheType.ENTITY ) {
if ( params.hasMoreTokens() ) {
lazyProperty = "all".equalsIgnoreCase( params.nextToken() );
}
}
else {
lazyProperty = false;
}
final CacheRegionDefinition def = new CacheRegionDefinition( cacheType, role, usage, region, lazyProperty );
cacheRegionDefinitions.add( def );
}
@SuppressWarnings("unchecked")
private ScanResult scan(BootstrapServiceRegistry bootstrapServiceRegistry) {
final Scanner scanner = locateOrBuildScanner( bootstrapServiceRegistry );
final ScanOptions scanOptions = determineScanOptions();
return scanner.scan( persistenceUnit, scanOptions );
}
private ScanOptions determineScanOptions() {
return new StandardScanOptions(
(String) configurationValues.get( AvailableSettings.AUTODETECTION ),
persistenceUnit.isExcludeUnlistedClasses()
);
}
@SuppressWarnings("unchecked")
private Scanner locateOrBuildScanner(BootstrapServiceRegistry bootstrapServiceRegistry) {
final Object value = configurationValues.remove( AvailableSettings.SCANNER );
if ( value == null ) {
return new StandardScanner();
}
if ( Scanner.class.isInstance( value ) ) {
return (Scanner) value;
}
Class<? extends Scanner> scannerClass;
if ( Class.class.isInstance( value ) ) {
try {
scannerClass = (Class<? extends Scanner>) value;
}
catch ( ClassCastException e ) {
throw persistenceException( "Expecting Scanner implementation, but found " + ((Class) value).getName() );
}
}
else {
final String scannerClassName = value.toString();
try {
scannerClass = bootstrapServiceRegistry.getService( ClassLoaderService.class ).classForName( scannerClassName );
}
catch ( ClassCastException e ) {
throw persistenceException( "Expecting Scanner implementation, but found " + scannerClassName );
}
}
try {
return scannerClass.newInstance();
}
catch ( Exception e ) {
throw persistenceException( "Unable to instantiate Scanner class: " + scannerClass, e );
}
}
@Override
public EntityManagerFactoryBuilder withValidatorFactory(Object validatorFactory) {
this.validatorFactory = validatorFactory;
if ( validatorFactory != null ) {
BeanValidationIntegrator.validateFactory( validatorFactory );
}
return this;
}
@Override
public EntityManagerFactoryBuilder withDataSource(DataSource dataSource) {
this.dataSource = dataSource;
return this;
}
@Override
public void cancel() {
}
@Override
public void generateSchema() {
processProperties();
final ServiceRegistry serviceRegistry = buildServiceRegistry();
final ClassLoaderService classLoaderService = serviceRegistry.getService( ClassLoaderService.class );
( (ClassLoaderServiceImpl) classLoaderService ).withTccl(
new ClassLoaderServiceImpl.Work() {
@Override
public Object perform() {
final Configuration hibernateConfiguration = buildHibernateConfiguration( serviceRegistry );
try {
hibernateConfiguration.buildSessionFactory( serviceRegistry );
}
catch (MappingException e) {
throw persistenceException( "Unable to build Hibernate SessionFactory", e );
}
JpaSchemaGenerator.performGeneration( hibernateConfiguration, serviceRegistry );
return null;
}
@Override
public Map getConfigurationValues() {
return configurationValues;
}
}
);
cancel();
}
@SuppressWarnings("unchecked")
public EntityManagerFactory build() {
processProperties();
final ServiceRegistry serviceRegistry = buildServiceRegistry();
final ClassLoaderService classLoaderService = serviceRegistry.getService( ClassLoaderService.class );
return ( (ClassLoaderServiceImpl) classLoaderService ).withTccl(
new ClassLoaderServiceImpl.Work<EntityManagerFactoryImpl>() {
@Override
public EntityManagerFactoryImpl perform() {
hibernateConfiguration = buildHibernateConfiguration( serviceRegistry );
SessionFactoryImplementor sessionFactory;
try {
sessionFactory = (SessionFactoryImplementor) hibernateConfiguration.buildSessionFactory( serviceRegistry );
}
catch (MappingException e) {
throw persistenceException( "Unable to build Hibernate SessionFactory", e );
}
JpaSchemaGenerator.performGeneration( hibernateConfiguration, serviceRegistry );
if ( suppliedSessionFactoryObserver != null ) {
sessionFactory.addObserver( suppliedSessionFactoryObserver );
}
sessionFactory.addObserver( new ServiceRegistryCloser() );
return new EntityManagerFactoryImpl(
persistenceUnit.getName(),
sessionFactory,
settings,
configurationValues,
hibernateConfiguration
);
}
@Override
public Map getConfigurationValues() {
return configurationValues;
}
}
);
}
private void processProperties() {
applyJdbcConnectionProperties();
applyTransactionProperties();
Object validationFactory = this.validatorFactory;
if ( validationFactory == null ) {
validationFactory = configurationValues.get( AvailableSettings.VALIDATION_FACTORY );
}
if ( validationFactory != null ) {
BeanValidationIntegrator.validateFactory( validationFactory );
serviceRegistryBuilder.applySetting( AvailableSettings.VALIDATION_FACTORY, validationFactory );
configurationValues.put( AvailableSettings.VALIDATION_FACTORY, this.validatorFactory );
}
if ( "true".equals( configurationValues.get( Environment.FLUSH_BEFORE_COMPLETION ) ) ) {
serviceRegistryBuilder.applySetting( Environment.FLUSH_BEFORE_COMPLETION, "false" );
LOG.definingFlushBeforeCompletionIgnoredInHem( Environment.FLUSH_BEFORE_COMPLETION );
}
final StrategySelector strategySelector = serviceRegistryBuilder.getBootstrapServiceRegistry().getService( StrategySelector.class );
for ( Object oEntry : configurationValues.entrySet() ) {
Map.Entry entry = (Map.Entry) oEntry;
if ( entry.getKey() instanceof String ) {
final String keyString = (String) entry.getKey();
if ( AvailableSettings.INTERCEPTOR.equals( keyString ) ) {
sessionFactoryInterceptor = strategySelector.resolveStrategy( Interceptor.class, entry.getValue() );
}
else if ( AvailableSettings.SESSION_INTERCEPTOR.equals( keyString ) ) {
settings.setSessionInterceptorClass(
loadSessionInterceptorClass( entry.getValue(), strategySelector )
);
}
else if ( AvailableSettings.NAMING_STRATEGY.equals( keyString ) ) {
namingStrategy = strategySelector.resolveStrategy( NamingStrategy.class, entry.getValue() );
}
else if ( AvailableSettings.NAMING_STRATEGY_DELEGATOR.equals( keyString ) ) {
namingStrategyDelegator = strategySelector.resolveStrategy( NamingStrategyDelegator.class, entry.getValue() );
}
else if ( AvailableSettings.SESSION_FACTORY_OBSERVER.equals( keyString ) ) {
suppliedSessionFactoryObserver = strategySelector.resolveStrategy( SessionFactoryObserver.class, entry.getValue() );
}
else if ( AvailableSettings.DISCARD_PC_ON_CLOSE.equals( keyString ) ) {
settings.setReleaseResourcesOnCloseEnabled( "true".equals( entry.getValue() ) );
}
else if ( keyString.startsWith( AvailableSettings.CLASS_CACHE_PREFIX ) ) {
addCacheRegionDefinition(
keyString.substring( AvailableSettings.CLASS_CACHE_PREFIX.length() + 1 ),
(String) entry.getValue(),
CacheRegionDefinition.CacheType.ENTITY
);
}
else if ( keyString.startsWith( AvailableSettings.COLLECTION_CACHE_PREFIX ) ) {
addCacheRegionDefinition(
keyString.substring( AvailableSettings.COLLECTION_CACHE_PREFIX.length() + 1 ),
(String) entry.getValue(),
CacheRegionDefinition.CacheType.COLLECTION
);
}
else if ( keyString.startsWith( AvailableSettings.JACC_PREFIX )
&& ! ( keyString.equals( AvailableSettings.JACC_CONTEXT_ID )
|| keyString.equals( AvailableSettings.JACC_ENABLED ) ) ) {
addJaccDefinition( (String) entry.getKey(), entry.getValue() );
}
}
}
}
private void applyJdbcConnectionProperties() {
if ( dataSource != null ) {
serviceRegistryBuilder.applySetting( org.hibernate.cfg.AvailableSettings.DATASOURCE, dataSource );
}
else if ( persistenceUnit.getJtaDataSource() != null ) {
if ( ! serviceRegistryBuilder.getSettings().containsKey( org.hibernate.cfg.AvailableSettings.DATASOURCE ) ) {
serviceRegistryBuilder.applySetting( org.hibernate.cfg.AvailableSettings.DATASOURCE, persistenceUnit.getJtaDataSource() );
configurationValues.put( AvailableSettings.JTA_DATASOURCE, persistenceUnit.getJtaDataSource() );
}
}
else if ( persistenceUnit.getNonJtaDataSource() != null ) {
if ( ! serviceRegistryBuilder.getSettings().containsKey( org.hibernate.cfg.AvailableSettings.DATASOURCE ) ) {
serviceRegistryBuilder.applySetting( org.hibernate.cfg.AvailableSettings.DATASOURCE, persistenceUnit.getNonJtaDataSource() );
configurationValues.put( AvailableSettings.NON_JTA_DATASOURCE, persistenceUnit.getNonJtaDataSource() );
}
}
else {
final String driver = (String) configurationValues.get( AvailableSettings.JDBC_DRIVER );
if ( StringHelper.isNotEmpty( driver ) ) {
serviceRegistryBuilder.applySetting( org.hibernate.cfg.AvailableSettings.DRIVER, driver );
}
final String url = (String) configurationValues.get( AvailableSettings.JDBC_URL );
if ( StringHelper.isNotEmpty( url ) ) {
serviceRegistryBuilder.applySetting( org.hibernate.cfg.AvailableSettings.URL, url );
}
final String user = (String) configurationValues.get( AvailableSettings.JDBC_USER );
if ( StringHelper.isNotEmpty( user ) ) {
serviceRegistryBuilder.applySetting( org.hibernate.cfg.AvailableSettings.USER, user );
}
final String pass = (String) configurationValues.get( AvailableSettings.JDBC_PASSWORD );
if ( StringHelper.isNotEmpty( pass ) ) {
serviceRegistryBuilder.applySetting( org.hibernate.cfg.AvailableSettings.PASS, pass );
}
}
}
private void applyTransactionProperties() {
PersistenceUnitTransactionType txnType = PersistenceUnitTransactionTypeHelper.interpretTransactionType(
configurationValues.get( AvailableSettings.TRANSACTION_TYPE )
);
if ( txnType == null ) {
txnType = persistenceUnit.getTransactionType();
}
if ( txnType == null ) {
txnType = PersistenceUnitTransactionType.RESOURCE_LOCAL;
}
settings.setTransactionType( txnType );
boolean hasTxStrategy = configurationValues.containsKey( Environment.TRANSACTION_STRATEGY );
if ( hasTxStrategy ) {
LOG.overridingTransactionStrategyDangerous( Environment.TRANSACTION_STRATEGY );
}
else {
if ( txnType == PersistenceUnitTransactionType.JTA ) {
serviceRegistryBuilder.applySetting( Environment.TRANSACTION_STRATEGY, CMTTransactionFactory.class );
}
else if ( txnType == PersistenceUnitTransactionType.RESOURCE_LOCAL ) {
serviceRegistryBuilder.applySetting( Environment.TRANSACTION_STRATEGY, JdbcTransactionFactory.class );
}
}
}
@SuppressWarnings("unchecked")
private Class<? extends Interceptor> loadSessionInterceptorClass(Object value, StrategySelector strategySelector) {
if ( value == null ) {
return null;
}
return Class.class.isInstance( value )
? (Class<? extends Interceptor>) value
: strategySelector.selectStrategyImplementor( Interceptor.class, value.toString() );
}
public ServiceRegistry buildServiceRegistry() {
return serviceRegistryBuilder.build();
}
public Configuration buildHibernateConfiguration(ServiceRegistry serviceRegistry) {
Properties props = new Properties();
props.putAll( configurationValues );
Configuration cfg = new Configuration();
cfg.getProperties().putAll( props );
cfg.setEntityNotFoundDelegate( jpaEntityNotFoundDelegate );
if ( namingStrategy != null && namingStrategyDelegator != null ) {
throw persistenceException(
AvailableSettings.NAMING_STRATEGY + " and " + AvailableSettings.NAMING_STRATEGY_DELEGATOR +
" properties cannot be used together. To be valid, only one of these properties can be set."
);
}
if ( namingStrategy != null ) {
cfg.setNamingStrategy( namingStrategy );
}
else if ( namingStrategyDelegator != null ) {
cfg.setNamingStrategyDelegator( namingStrategyDelegator );
}
if ( sessionFactoryInterceptor != null ) {
cfg.setInterceptor( sessionFactoryInterceptor );
}
final Object strategyProviderValue = props.get( AvailableSettings.IDENTIFIER_GENERATOR_STRATEGY_PROVIDER );
final IdentifierGeneratorStrategyProvider strategyProvider = strategyProviderValue == null
? null
: serviceRegistry.getService( StrategySelector.class )
.resolveStrategy( IdentifierGeneratorStrategyProvider.class, strategyProviderValue );
if ( strategyProvider != null ) {
final MutableIdentifierGeneratorFactory identifierGeneratorFactory = cfg.getIdentifierGeneratorFactory();
for ( Map.Entry<String,Class<?>> entry : strategyProvider.getStrategies().entrySet() ) {
identifierGeneratorFactory.register( entry.getKey(), entry.getValue() );
}
}
if ( grantedJaccPermissions != null ) {
final JaccService jaccService = serviceRegistry.getService( JaccService.class );
for ( GrantedPermission grantedPermission : grantedJaccPermissions ) {
jaccService.addPermission( grantedPermission );
}
}
if ( cacheRegionDefinitions != null ) {
for ( CacheRegionDefinition cacheRegionDefinition : cacheRegionDefinitions ) {
if ( cacheRegionDefinition.cacheType == CacheRegionDefinition.CacheType.ENTITY ) {
cfg.setCacheConcurrencyStrategy(
cacheRegionDefinition.role,
cacheRegionDefinition.usage,
cacheRegionDefinition.region,
cacheRegionDefinition.cacheLazy
);
}
else {
cfg.setCollectionCacheConcurrencyStrategy(
cacheRegionDefinition.role,
cacheRegionDefinition.usage,
cacheRegionDefinition.region
);
}
}
}
for ( JaxbHibernateConfiguration.JaxbSessionFactory.JaxbMapping jaxbMapping : cfgXmlNamedMappings ) {
if ( jaxbMapping.getClazz() != null ) {
cfg.addAnnotatedClass(
serviceRegistry.getService( ClassLoaderService.class ).classForName( jaxbMapping.getClazz() )
);
}
else if ( jaxbMapping.getResource() != null ) {
cfg.addResource( jaxbMapping.getResource() );
}
else if ( jaxbMapping.getJar() != null ) {
cfg.addJar( new File( jaxbMapping.getJar() ) );
}
else if ( jaxbMapping.getPackage() != null ) {
cfg.addPackage( jaxbMapping.getPackage() );
}
}
List<Class> loadedAnnotatedClasses = (List<Class>) configurationValues.remove( AvailableSettings.LOADED_CLASSES );
if ( loadedAnnotatedClasses != null ) {
for ( Class cls : loadedAnnotatedClasses ) {
if ( AttributeConverter.class.isAssignableFrom( cls ) ) {
cfg.addAttributeConverter( (Class<? extends AttributeConverter>) cls );
}
else {
cfg.addAnnotatedClass( cls );
}
}
}
for ( String className : metadataSources.getAnnotatedMappingClassNames() ) {
cfg.addAnnotatedClass( serviceRegistry.getService( ClassLoaderService.class ).classForName( className ) );
}
for ( MetadataSources.ConverterDescriptor converterDescriptor : metadataSources.getConverterDescriptors() ) {
final Class<? extends AttributeConverter> converterClass;
try {
Class theClass = serviceRegistry.getService( ClassLoaderService.class ).classForName( converterDescriptor.converterClassName );
converterClass = (Class<? extends AttributeConverter>) theClass;
}
catch (ClassCastException e) {
throw persistenceException(
String.format(
"AttributeConverter implementation [%s] does not implement AttributeConverter interface",
converterDescriptor.converterClassName
)
);
}
cfg.addAttributeConverter( converterClass, converterDescriptor.autoApply );
}
for ( String resourceName : metadataSources.mappingFileResources ) {
Boolean useMetaInf = null;
try {
if ( resourceName.endsWith( META_INF_ORM_XML ) ) {
useMetaInf = true;
}
cfg.addResource( resourceName );
}
catch( MappingNotFoundException e ) {
if ( ! resourceName.endsWith( META_INF_ORM_XML ) ) {
throw persistenceException( "Unable to find XML mapping file in classpath: " + resourceName );
}
else {
useMetaInf = false;
}
}
catch( MappingException me ) {
throw persistenceException( "Error while reading JPA XML file: " + resourceName, me );
}
if ( Boolean.TRUE.equals( useMetaInf ) ) {
LOG.exceptionHeaderFound( getExceptionHeader(), META_INF_ORM_XML );
}
else if (Boolean.FALSE.equals(useMetaInf)) {
LOG.exceptionHeaderNotFound( getExceptionHeader(), META_INF_ORM_XML );
}
}
for ( NamedInputStream namedInputStream : metadataSources.namedMappingFileInputStreams ) {
try {
cfg.addInputStream( new BufferedInputStream( namedInputStream.getStream() ) );
}
catch ( InvalidMappingException e ) {
if ( StringHelper.isNotEmpty( namedInputStream.getName() ) ) {
throw new InvalidMappingException(
"Error while parsing file: " + namedInputStream.getName(),
e.getType(),
e.getPath(),
e
);
}
else {
throw e;
}
}
catch (MappingException me) {
if ( StringHelper.isNotEmpty( namedInputStream.getName() ) ) {
throw new MappingException("Error while parsing file: " + namedInputStream.getName(), me );
}
else {
throw me;
}
}
}
for ( String packageName : metadataSources.packageNames ) {
cfg.addPackage( packageName );
}
final TypeContributorList typeContributorList
= (TypeContributorList) configurationValues.get( TYPE_CONTRIBUTORS );
if ( typeContributorList != null ) {
configurationValues.remove( TYPE_CONTRIBUTORS );
for ( TypeContributor typeContributor : typeContributorList.getTypeContributors() ) {
cfg.registerTypeContributor( typeContributor );
}
}
return cfg;
}
public static class ServiceRegistryCloser implements SessionFactoryObserver {
@Override
public void sessionFactoryCreated(SessionFactory sessionFactory) {
}
@Override
public void sessionFactoryClosed(SessionFactory sessionFactory) {
SessionFactoryImplementor sfi = ( (SessionFactoryImplementor) sessionFactory );
sfi.getServiceRegistry().destroy();
ServiceRegistry basicRegistry = sfi.getServiceRegistry().getParentServiceRegistry();
( (ServiceRegistryImplementor) basicRegistry ).destroy();
}
}
private PersistenceException persistenceException(String message) {
return persistenceException( message, null );
}
private PersistenceException persistenceException(String message, Exception cause) {
return new PersistenceException(
getExceptionHeader() + message,
cause
);
}
private String () {
return "[PersistenceUnit: " + persistenceUnit.getName() + "] ";
}
public static class CacheRegionDefinition {
public static enum CacheType { ENTITY, COLLECTION }
public final CacheType cacheType;
public final String role;
public final String usage;
public final String region;
public final boolean cacheLazy;
public CacheRegionDefinition(
CacheType cacheType,
String role,
String usage,
String region, boolean cacheLazy) {
this.cacheType = cacheType;
this.role = role;
this.usage = usage;
this.region = region;
this.cacheLazy = cacheLazy;
}
}
public static class JaccDefinition {
public final String contextId;
public final String role;
public final String clazz;
public final String actions;
public JaccDefinition(String contextId, String role, String clazz, String actions) {
this.contextId = contextId;
this.role = role;
this.clazz = clazz;
this.actions = actions;
}
}
public static class MetadataSources {
private final List<String> annotatedMappingClassNames = new ArrayList<String>();
private final List<ConverterDescriptor> converterDescriptors = new ArrayList<ConverterDescriptor>();
private final List<NamedInputStream> namedMappingFileInputStreams = new ArrayList<NamedInputStream>();
private final List<String> mappingFileResources = new ArrayList<String>();
private final List<String> packageNames = new ArrayList<String>();
public List<String> getAnnotatedMappingClassNames() {
return annotatedMappingClassNames;
}
public List<ConverterDescriptor> getConverterDescriptors() {
return converterDescriptors;
}
public List<NamedInputStream> getNamedMappingFileInputStreams() {
return namedMappingFileInputStreams;
}
public List<String> getPackageNames() {
return packageNames;
}
public List<String> collectMappingClassNames() {
return annotatedMappingClassNames;
}
public static class ConverterDescriptor {
private final String converterClassName;
private final boolean autoApply;
public ConverterDescriptor(String converterClassName, boolean autoApply) {
this.converterClassName = converterClassName;
this.autoApply = autoApply;
}
}
}
}