Package org.hibernate.ejb

Source Code of org.hibernate.ejb.Ejb3Configuration

// $Id$
/*
* Copyright (c) 2009, Red Hat Middleware LLC 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 Middleware LLC.
*
* 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.ejb;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
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.naming.BinaryRefAddr;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityNotFoundException;
import javax.persistence.MappedSuperclass;
import javax.persistence.PersistenceException;
import javax.persistence.spi.PersistenceUnitInfo;
import javax.persistence.spi.PersistenceUnitTransactionType;
import javax.sql.DataSource;

import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;

import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
import org.hibernate.MappingException;
import org.hibernate.MappingNotFoundException;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.cfg.NamingStrategy;
import org.hibernate.cfg.Settings;
import org.hibernate.cfg.SettingsFactory;
import org.hibernate.cfg.annotations.reflection.XMLContext;
import org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider;
import org.hibernate.ejb.instrument.InterceptFieldClassFileTransformer;
import org.hibernate.ejb.packaging.JarVisitorFactory;
import org.hibernate.ejb.packaging.NamedInputStream;
import org.hibernate.ejb.packaging.NativeScanner;
import org.hibernate.ejb.packaging.PersistenceMetadata;
import org.hibernate.ejb.packaging.PersistenceXmlLoader;
import org.hibernate.ejb.packaging.Scanner;
import org.hibernate.ejb.transaction.JoinableCMTTransactionFactory;
import org.hibernate.ejb.util.ConfigurationHelper;
import org.hibernate.ejb.util.LogHelper;
import org.hibernate.ejb.util.NamingHelper;
import org.hibernate.engine.FilterDefinition;
import org.hibernate.event.EventListeners;
import org.hibernate.mapping.AuxiliaryDatabaseObject;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.persister.PersisterClassProvider;
import org.hibernate.proxy.EntityNotFoundDelegate;
import org.hibernate.secure.JACCConfiguration;
import org.hibernate.transaction.JDBCTransactionFactory;
import org.hibernate.util.CollectionHelper;
import org.hibernate.util.ReflectHelper;
import org.hibernate.util.StringHelper;
import org.hibernate.util.xml.MappingReader;
import org.hibernate.util.xml.OriginImpl;
import org.hibernate.util.xml.XmlDocument;

/**
* Allow a fine tuned configuration of an EJB 3.0 EntityManagerFactory
*
* A Ejb3Configuration object is only guaranteed to create one EntityManagerFactory.
* Multiple usage of #buildEntityManagerFactory() is not guaranteed.
*
* After #buildEntityManagerFactory() has been called, you no longer can change the configuration
* state (no class adding, no property change etc)
*
* When serialized / deserialized or retrieved from the JNDI, you no longer can change the
* configuration state (no class adding, no property change etc)
*
* Putting the configuration in the JNDI is an expensive operation that requires a partial
* serialization
*
* @author Emmanuel Bernard
*/
public class Ejb3Configuration implements Serializable, Referenceable {
  private final Logger log = LoggerFactory.getLogger( Ejb3Configuration.class );
  private static final String IMPLEMENTATION_NAME = HibernatePersistence.class.getName();
  private static final String META_INF_ORM_XML = "META-INF/orm.xml";
  private static final String PARSED_MAPPING_DOMS = "hibernate.internal.mapping_doms";

  private static EntityNotFoundDelegate ejb3EntityNotFoundDelegate = new Ejb3EntityNotFoundDelegate();
  private static Configuration DEFAULT_CONFIGURATION = new AnnotationConfiguration();

  private static class Ejb3EntityNotFoundDelegate implements EntityNotFoundDelegate, Serializable {
    public void handleEntityNotFound(String entityName, Serializable id) {
      throw new EntityNotFoundException("Unable to find " + entityName  + " with id " + id);
    }
  }

  static {
    Version.touch();
  }

  private String persistenceUnitName;
  private String cfgXmlResource;

  private AnnotationConfiguration cfg;
  private SettingsFactory settingsFactory;
  //made transient and not restored in deserialization on purpose, should no longer be called after restoration
  private transient EventListenerConfigurator listenerConfigurator;
  private PersistenceUnitTransactionType transactionType;
  private boolean discardOnClose;
  //made transient and not restored in deserialization on purpose, should no longer be called after restoration
  private transient ClassLoader overridenClassLoader;
  private boolean isConfigurationProcessed = false;


  public Ejb3Configuration() {
    settingsFactory = new InjectionSettingsFactory();
    cfg = new AnnotationConfiguration( settingsFactory );
    cfg.setEntityNotFoundDelegate( ejb3EntityNotFoundDelegate );
    listenerConfigurator = new EventListenerConfigurator( this );
  }

  /**
   * Used to inject a datasource object as the connection provider.
   * If used, be sure to <b>not override</b> the hibernate.connection.provider_class
   * property
   */
  @SuppressWarnings({ "JavaDoc", "unchecked" })
  public void setDataSource(DataSource ds) {
    if ( ds != null ) {
      Map cpInjection = new HashMap();
      cpInjection.put( "dataSource", ds );
      ( (InjectionSettingsFactory) settingsFactory ).setConnectionProviderInjectionData( cpInjection );
      this.setProperty( Environment.CONNECTION_PROVIDER, InjectedDataSourceConnectionProvider.class.getName() );
    }
  }

  /**
   * create a factory from a parsed persistence.xml
   * Especially the scanning of classes and additional jars is done already at this point.
   * <p/>
   * NOTE: public only for unit testing purposes; not a public API!
   *
   * @param metadata The information parsed from the persistence.xml
   * @param overridesIn Any explicitly passed config settings
   *
   * @return this
   */
  @SuppressWarnings({ "unchecked" })
  public Ejb3Configuration configure(PersistenceMetadata metadata, Map overridesIn) {
    log.debug( "Creating Factory: {}", metadata.getName() );

    Map overrides = new HashMap();
    if ( overridesIn != null ) {
      overrides.putAll( overridesIn );
    }

    Map workingVars = new HashMap();
    workingVars.put( AvailableSettings.PERSISTENCE_UNIT_NAME, metadata.getName() );
    this.persistenceUnitName = metadata.getName();

    if ( StringHelper.isNotEmpty( metadata.getJtaDatasource() ) ) {
      this.setProperty( Environment.DATASOURCE, metadata.getJtaDatasource() );
    }
    else if ( StringHelper.isNotEmpty( metadata.getNonJtaDatasource() ) ) {
      this.setProperty( Environment.DATASOURCE, metadata.getNonJtaDatasource() );
    }
    else {
      final String driver = (String) metadata.getProps().get( AvailableSettings.JDBC_DRIVER );
      if ( StringHelper.isNotEmpty( driver ) ) {
        this.setProperty( Environment.DRIVER, driver );
      }
      final String url = (String) metadata.getProps().get( AvailableSettings.JDBC_URL );
      if ( StringHelper.isNotEmpty( url ) ) {
        this.setProperty( Environment.URL, url );
      }
      final String user = (String) metadata.getProps().get( AvailableSettings.JDBC_USER );
      if ( StringHelper.isNotEmpty( user ) ) {
        this.setProperty( Environment.USER, user );
      }
      final String pass = (String) metadata.getProps().get( AvailableSettings.JDBC_PASSWORD );
      if ( StringHelper.isNotEmpty( pass ) ) {
        this.setProperty( Environment.PASS, pass );
      }
    }
    defineTransactionType( metadata.getTransactionType(), workingVars );
    if ( metadata.getClasses().size() > 0 ) {
      workingVars.put( AvailableSettings.CLASS_NAMES, metadata.getClasses() );
    }
    if ( metadata.getPackages().size() > 0 ) {
      workingVars.put( AvailableSettings.PACKAGE_NAMES, metadata.getPackages() );
    }
    if ( metadata.getMappingFiles().size() > 0 ) {
      workingVars.put( AvailableSettings.XML_FILE_NAMES, metadata.getMappingFiles() );
    }
    if ( metadata.getHbmfiles().size() > 0 ) {
      workingVars.put( AvailableSettings.HBXML_FILES, metadata.getHbmfiles() );
    }

    Properties props = new Properties();
    props.putAll( metadata.getProps() );

    // validation factory
    final Object validationFactory = overrides.get( AvailableSettings.VALIDATION_FACTORY );
    if ( validationFactory != null ) {
      props.put( AvailableSettings.VALIDATION_FACTORY, validationFactory );
    }
    overrides.remove( AvailableSettings.VALIDATION_FACTORY );

    // validation-mode (overrides has precedence)
    {
      final Object integrationValue = overrides.get( AvailableSettings.VALIDATION_MODE );
      if ( integrationValue != null ) {
        props.put( AvailableSettings.VALIDATION_MODE, integrationValue.toString() );
      }
      else if ( metadata.getValidationMode() != null ) {
        props.put( AvailableSettings.VALIDATION_MODE, metadata.getValidationMode() );
      }
      overrides.remove( AvailableSettings.VALIDATION_MODE );
    }

    // shared-cache-mode (overrides has precedence)
    {
      final Object integrationValue = overrides.get( AvailableSettings.SHARED_CACHE_MODE );
      if ( integrationValue != null ) {
        props.put( AvailableSettings.SHARED_CACHE_MODE, integrationValue.toString() );
      }
      else if ( metadata.getSharedCacheMode() != null ) {
        props.put( AvailableSettings.SHARED_CACHE_MODE, metadata.getSharedCacheMode() );
      }
      overrides.remove( AvailableSettings.SHARED_CACHE_MODE );
    }

    for ( Map.Entry entry : (Set<Map.Entry>) overrides.entrySet() ) {
      Object value = entry.getValue();
      props.put( entry.getKey(), value == null ? "" :  value ); //alter null, not allowed in properties
    }

    configure( props, workingVars );
    return this;
  }

  /**
   * Build the configuration from an entity manager name and given the
   * appropriate extra properties. Those properties override the one get through
   * the peristence.xml file.
   * If the persistence unit name is not found or does not match the Persistence Provider, null is returned
   *
   * This method is used in a non managed environment
   *
   * @param persistenceUnitName persistence unit name
   * @param integration properties passed to the persistence provider
   *
   * @return configured Ejb3Configuration or null if no persistence unit match
   *
   * @see HibernatePersistence#createEntityManagerFactory(String, java.util.Map)
   */
  @SuppressWarnings({ "unchecked" })
  public Ejb3Configuration configure(String persistenceUnitName, Map integration) {
    try {
      log.debug( "Look up for persistence unit: {}", persistenceUnitName );
      integration = integration == null ?
          CollectionHelper.EMPTY_MAP :
          Collections.unmodifiableMap( integration );
      Enumeration<URL> xmls = Thread.currentThread()
          .getContextClassLoader()
          .getResources( "META-INF/persistence.xml" );
      if ( ! xmls.hasMoreElements() ) {
        log.info( "Could not find any META-INF/persistence.xml file in the classpath");
      }
      while ( xmls.hasMoreElements() ) {
        URL url = xmls.nextElement();
        log.trace( "Analysing persistence.xml: {}", url );
        List<PersistenceMetadata> metadataFiles = PersistenceXmlLoader.deploy(
            url,
            integration,
            cfg.getEntityResolver(),
            PersistenceUnitTransactionType.RESOURCE_LOCAL );
        for ( PersistenceMetadata metadata : metadataFiles ) {
          log.trace( "{}", metadata );

          if ( metadata.getProvider() == null || IMPLEMENTATION_NAME.equalsIgnoreCase(
              metadata.getProvider()
          ) ) {
            //correct provider

            //lazy load the scanner to avoid unnecessary IOExceptions
            Scanner scanner = null;
            URL jarURL = null;
            if ( metadata.getName() == null ) {
              scanner = buildScanner( metadata.getProps(), integration );
              jarURL = JarVisitorFactory.getJarURLFromURLEntry( url, "/META-INF/persistence.xml" );
              metadata.setName( scanner.getUnqualifiedJarName(jarURL) );
            }
            if ( persistenceUnitName == null && xmls.hasMoreElements() ) {
              throw new PersistenceException( "No name provided and several persistence units found" );
            }
            else if ( persistenceUnitName == null || metadata.getName().equals( persistenceUnitName ) ) {
              if (scanner == null) {
                scanner = buildScanner( metadata.getProps(), integration );
                jarURL = JarVisitorFactory.getJarURLFromURLEntry( url, "/META-INF/persistence.xml" );
              }
              //scan main JAR
              ScanningContext mainJarScanCtx = new ScanningContext()
                  .scanner( scanner )
                  .url( jarURL )
                  .explicitMappingFiles( metadata.getMappingFiles() )
                  .searchOrm( true );
              setDetectedArtifactsOnScanningContext( mainJarScanCtx, metadata.getProps(), integration,
                                        metadata.getExcludeUnlistedClasses() );
              addMetadataFromScan( mainJarScanCtx, metadata );

              ScanningContext otherJarScanCtx = new ScanningContext()
                  .scanner( scanner )
                  .explicitMappingFiles( metadata.getMappingFiles() )
                  .searchOrm( true );
              setDetectedArtifactsOnScanningContext( otherJarScanCtx, metadata.getProps(), integration,
                                        false );
              for ( String jarFile : metadata.getJarFiles() ) {
                otherJarScanCtx.url( JarVisitorFactory.getURLFromPath( jarFile ) );
                addMetadataFromScan( otherJarScanCtx, metadata );
              }
              return configure( metadata, integration );
            }
          }
        }
      }
      return null;
    }
    catch (Exception e) {
      if ( e instanceof PersistenceException) {
        throw (PersistenceException) e;
      }
      else {
        throw new PersistenceException( getExceptionHeader() + "Unable to configure EntityManagerFactory", e );
      }
    }
  }

  private Scanner buildScanner(Properties properties, Map<?,?> integration) {
    //read the String or Instance from the integration map first and use the properties as a backup.
    Object scanner = integration.get( AvailableSettings.SCANNER );
    if (scanner == null) {
      scanner = properties.getProperty( AvailableSettings.SCANNER );
    }
    if (scanner != null) {
      Class<?> scannerClass;
      if ( scanner instanceof String ) {
        try {
          scannerClass = ReflectHelper.classForName( (String) scanner, this.getClass() );
        }
        catch ( ClassNotFoundException e ) {
          throw new PersistenceException"Cannot find scanner class. " + AvailableSettings.SCANNER + "=" + scanner, e );
        }
      }
      else if (scanner instanceof Class) {
        scannerClass = (Class<? extends Scanner>) scanner;
      }
      else if (scanner instanceof Scanner) {
        return (Scanner) scanner;
      }
      else {
        throw new PersistenceException"Scanner class configuration error: unknown type on the property. " + AvailableSettings.SCANNER );
      }
      try {
        return (Scanner) scannerClass.newInstance();
      }
      catch ( InstantiationException e ) {
        throw new PersistenceException"Unable to load Scanner class: " + scannerClass, e );
      }
      catch ( IllegalAccessException e ) {
        throw new PersistenceException"Unable to load Scanner class: " + scannerClass, e );
      }
    }
    else {
      return new NativeScanner();
    }
  }

  private static class ScanningContext {
    //boolean excludeUnlistedClasses;
    private Scanner scanner;
    private URL url;
    private List<String> explicitMappingFiles;
    private boolean detectClasses;
    private boolean detectHbmFiles;
    private boolean searchOrm;

    public ScanningContext scanner(Scanner scanner) {
      this.scanner = scanner;
      return this;
    }

    public ScanningContext url(URL url) {
      this.url = url;
      return this;
    }

    public ScanningContext explicitMappingFiles(List<String> explicitMappingFiles) {
      this.explicitMappingFiles = explicitMappingFiles;
      return this;
    }

    public ScanningContext detectClasses(boolean detectClasses) {
      this.detectClasses = detectClasses;
      return this;
    }

    public ScanningContext detectHbmFiles(boolean detectHbmFiles) {
      this.detectHbmFiles = detectHbmFiles;
      return this;
    }

    public ScanningContext searchOrm(boolean searchOrm) {
      this.searchOrm = searchOrm;
      return this;
    }
  }

  private static void addMetadataFromScan(ScanningContext scanningContext, PersistenceMetadata metadata) throws IOException {
    List<String> classes = metadata.getClasses();
    List<String> packages = metadata.getPackages();
    List<NamedInputStream> hbmFiles = metadata.getHbmfiles();
    List<String> mappingFiles = metadata.getMappingFiles();
    addScannedEntries( scanningContext, classes, packages, hbmFiles, mappingFiles );
  }

  private static void addScannedEntries(ScanningContext scanningContext, List<String> classes, List<String> packages, List<NamedInputStream> hbmFiles, List<String> mappingFiles) throws IOException {
    Scanner scanner = scanningContext.scanner;
    if (scanningContext.detectClasses) {
      Set<Class<? extends Annotation>> annotationsToExclude = new HashSet<Class<? extends Annotation>>(3);
      annotationsToExclude.add( Entity.class );
      annotationsToExclude.add( MappedSuperclass.class );
      annotationsToExclude.add( Embeddable.class );
      Set<Class<?>> matchingClasses = scanner.getClassesInJar( scanningContext.url, annotationsToExclude );
      for (Class<?> clazz : matchingClasses) {
        classes.add( clazz.getName() );
      }

      Set<Package> matchingPackages = scanner.getPackagesInJar( scanningContext.url, new HashSet<Class<? extends Annotation>>(0) );
      for (Package pkg : matchingPackages) {
        packages.add( pkg.getName() );
      }
    }
    Set<String> patterns = new HashSet<String>();
    if (scanningContext.searchOrm) {
      patterns.add( META_INF_ORM_XML );
    }
    if (scanningContext.detectHbmFiles) {
      patterns.add( "**/*.hbm.xml" );
    }
    if ( mappingFiles != null) patterns.addAll( mappingFiles );
    if (patterns.size() !=0) {
      Set<NamedInputStream> files = scanner.getFilesInJar( scanningContext.url, patterns );
      for (NamedInputStream file : files) {
        hbmFiles.add( file );
        if (mappingFiles != null) mappingFiles.remove( file.getName() );
      }
    }
  }

  /**
   * Process configuration from a PersistenceUnitInfo object; typically called by the container
   * via {@link javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory}.
   * In Hibernate EM, this correlates to {@link HibernatePersistence#createContainerEntityManagerFactory}
   *
   * @param info The persistence unit info passed in by the container (usually from processing a persistence.xml).
   * @param integration The map of integration properties from the container to configure the provider.
   *
   * @return The configured EJB3Configurartion object
   *
   * @see HibernatePersistence#createContainerEntityManagerFactory
   */
  @SuppressWarnings({ "unchecked" })
  public Ejb3Configuration configure(PersistenceUnitInfo info, Map integration) {
    if ( log.isDebugEnabled() ) {
      log.debug( "Processing {}", LogHelper.logPersistenceUnitInfo( info ) );
    }
    else {
      log.info( "Processing PersistenceUnitInfo [\n\tname: {}\n\t...]", info.getPersistenceUnitName() );
    }

    // Spec says the passed map may be null, so handle that to make further processing easier...
    integration = integration != null ? Collections.unmodifiableMap( integration ) : CollectionHelper.EMPTY_MAP;

    // See if we (Hibernate) are the persistence provider
    String provider = (String) integration.get( AvailableSettings.PROVIDER );
    if ( provider == null ) {
      provider = info.getPersistenceProviderClassName();
    }
    if ( provider != null && ! provider.trim().startsWith( IMPLEMENTATION_NAME ) ) {
      log.info( "Required a different provider: {}", provider );
      return null;
    }

    // set the classloader, passed in by the container in info, to set as the TCCL so that
    // Hibernate uses it to properly resolve class references.
    if ( info.getClassLoader() == null ) {
      throw new IllegalStateException(
          "[PersistenceUnit: " + info.getPersistenceUnitName() == null ? "" : info.getPersistenceUnitName()
              + "] " + "PersistenceUnitInfo.getClassLoader() id null" );
    }
    Thread thread = Thread.currentThread();
    ClassLoader contextClassLoader = thread.getContextClassLoader();
    boolean sameClassLoader = info.getClassLoader().equals( contextClassLoader );
    if ( ! sameClassLoader ) {
      overridenClassLoader = info.getClassLoader();
      thread.setContextClassLoader( overridenClassLoader );
    }
    else {
      overridenClassLoader = null;
    }

    // Best I can tell, 'workingVars' is some form of additional configuration contract.
    // But it does not correlate 1-1 to EMF/SF settings.  It really is like a set of de-typed
    // additional configuration info.  I think it makes better sense to define this as an actual
    // contract if that was in fact the intent; the code here is pretty confusing.
    try {
      Map workingVars = new HashMap();
      workingVars.put( AvailableSettings.PERSISTENCE_UNIT_NAME, info.getPersistenceUnitName() );
      this.persistenceUnitName = info.getPersistenceUnitName();
      List<String> entities = new ArrayList<String>( 50 );
      if ( info.getManagedClassNames() != null ) entities.addAll( info.getManagedClassNames() );
      List<NamedInputStream> hbmFiles = new ArrayList<NamedInputStream>();
      List<String> packages = new ArrayList<String>();
      List<String> xmlFiles = new ArrayList<String>( 50 );
      List<XmlDocument> xmlDocuments = new ArrayList<XmlDocument>( 50 );
      if ( info.getMappingFileNames() != null ) {
        xmlFiles.addAll( info.getMappingFileNames() );
      }
      //Should always be true if the container is not dump
      boolean searchForORMFiles = ! xmlFiles.contains( META_INF_ORM_XML );

      ScanningContext context = new ScanningContext();
      final Properties copyOfProperties = (Properties) info.getProperties().clone();
      ConfigurationHelper.overrideProperties( copyOfProperties, integration );
      context.scanner( buildScanner( copyOfProperties, integration ) )
          .searchOrm( searchForORMFiles )
          .explicitMappingFiles( null ); //URLs provided by the container already

      //context for other JARs
      setDetectedArtifactsOnScanningContext(context, info.getProperties(), null, false );
      for ( URL jar : info.getJarFileUrls() ) {
        context.url(jar);
        scanForClasses( context, packages, entities, hbmFiles );
      }

      //main jar
      context.url( info.getPersistenceUnitRootUrl() );
      setDetectedArtifactsOnScanningContext( context, info.getProperties(), null, info.excludeUnlistedClasses() );
      scanForClasses( context, packages, entities, hbmFiles );

      Properties properties = info.getProperties() != null ? info.getProperties() : new Properties();
      ConfigurationHelper.overrideProperties( properties, integration );

      //FIXME entities is used to enhance classes and to collect annotated entities this should not be mixed
      //fill up entities with the on found in xml files
      addXMLEntities( xmlFiles, info, entities, xmlDocuments );

      //FIXME send the appropriate entites.
      if ( "true".equalsIgnoreCase( properties.getProperty( AvailableSettings.USE_CLASS_ENHANCER ) ) ) {
        info.addTransformer( new InterceptFieldClassFileTransformer( entities ) );
      }

      workingVars.put( AvailableSettings.CLASS_NAMES, entities );
      workingVars.put( AvailableSettings.PACKAGE_NAMES, packages );
      workingVars.put( AvailableSettings.XML_FILE_NAMES, xmlFiles );
      workingVars.put( PARSED_MAPPING_DOMS, xmlDocuments );

      if ( hbmFiles.size() > 0 ) {
        workingVars.put( AvailableSettings.HBXML_FILES, hbmFiles );
      }

      // validation factory
      final Object validationFactory = integration.get( AvailableSettings.VALIDATION_FACTORY );
      if ( validationFactory != null ) {
        properties.put( AvailableSettings.VALIDATION_FACTORY, validationFactory );
      }

      // validation-mode (integration has precedence)
      {
        final Object integrationValue = integration.get( AvailableSettings.VALIDATION_MODE );
        if ( integrationValue != null ) {
          properties.put( AvailableSettings.VALIDATION_MODE, integrationValue.toString() );
        }
        else if ( info.getValidationMode() != null ) {
          properties.put( AvailableSettings.VALIDATION_MODE, info.getValidationMode().name() );
        }
      }

      // shared-cache-mode (integration has precedence)
      {
        final Object integrationValue = integration.get( AvailableSettings.SHARED_CACHE_MODE );
        if ( integrationValue != null ) {
          properties.put( AvailableSettings.SHARED_CACHE_MODE, integrationValue.toString() );
        }
        else if ( info.getSharedCacheMode() != null ) {
          properties.put( AvailableSettings.SHARED_CACHE_MODE, info.getSharedCacheMode().name() );
        }
      }

      //datasources
      Boolean isJTA = null;
      boolean overridenDatasource = false;
      if ( integration.containsKey( AvailableSettings.JTA_DATASOURCE ) ) {
        String dataSource = (String) integration.get( AvailableSettings.JTA_DATASOURCE );
        overridenDatasource = true;
        properties.setProperty( Environment.DATASOURCE, dataSource );
        isJTA = Boolean.TRUE;
      }
      if ( integration.containsKey( AvailableSettings.NON_JTA_DATASOURCE ) ) {
        String dataSource = (String) integration.get( AvailableSettings.NON_JTA_DATASOURCE );
        overridenDatasource = true;
        properties.setProperty( Environment.DATASOURCE, dataSource );
        if (isJTA == null) isJTA = Boolean.FALSE;
      }

      if ( ! overridenDatasource && ( info.getJtaDataSource() != null || info.getNonJtaDataSource() != null ) ) {
        isJTA = info.getJtaDataSource() != null ? Boolean.TRUE : Boolean.FALSE;
        this.setDataSource(
            isJTA ? info.getJtaDataSource() : info.getNonJtaDataSource()
        );
        this.setProperty(
            Environment.CONNECTION_PROVIDER, InjectedDataSourceConnectionProvider.class.getName()
        );
      }
      /*
       * If explicit type => use it
       * If a JTA DS is used => JTA transaction,
       * if a non JTA DS is used => RESOURCe_LOCAL
       * if none, set to JavaEE default => JTA transaction
       */
      PersistenceUnitTransactionType transactionType = info.getTransactionType();
      if (transactionType == null) {
        if (isJTA == Boolean.TRUE) {
          transactionType = PersistenceUnitTransactionType.JTA;
        }
        else if ( isJTA == Boolean.FALSE ) {
          transactionType = PersistenceUnitTransactionType.RESOURCE_LOCAL;
        }
        else {
          transactionType = PersistenceUnitTransactionType.JTA;
        }
      }
      defineTransactionType( transactionType, workingVars );
      configure( properties, workingVars );
    }
    finally {
      //After EMF, set the CCL back
      if ( ! sameClassLoader ) {
        thread.setContextClassLoader( contextClassLoader );
      }
    }
    return this;
  }

  /**
   * Processes {@code xmlFiles} argument and populates:<ul>
   * <li>the {@code entities} list with encountered classnames</li>
   * <li>the {@code xmlDocuments} list with parsed/validated {@link XmlDocument} corrolary to each xml file</li>
   * </ul>
   *
   * @param xmlFiles The XML resource names; these will be resolved by classpath lookup and parsed/validated.
   * @param info The PUI
   * @param entities (output) The names of all encountered "mapped" classes
   * @param xmlDocuments (output) The list of {@link XmlDocument} instances of each entry in {@code xmlFiles}
   */
  @SuppressWarnings({ "unchecked" })
  private void addXMLEntities(
      List<String> xmlFiles,
      PersistenceUnitInfo info,
      List<String> entities,
      List<XmlDocument> xmlDocuments) {
    //TODO handle inputstream related hbm files
    ClassLoader classLoaderToUse = info.getNewTempClassLoader();
    if ( classLoaderToUse == null ) {
      log.warn(
          "Persistence provider caller does not implement the EJB3 spec correctly." +
              "PersistenceUnitInfo.getNewTempClassLoader() is null."
      );
      return;
    }
    for ( final String xmlFile : xmlFiles ) {
      final InputStream fileInputStream = classLoaderToUse.getResourceAsStream( xmlFile );
      if ( fileInputStream == null ) {
        log.info( "Unable to resolve mapping file [{}]", xmlFile );
        continue;
      }
      final InputSource inputSource = new InputSource( fileInputStream );

      XmlDocument metadataXml = MappingReader.INSTANCE.readMappingDocument(
          cfg.getEntityResolver(),
          inputSource,
          new OriginImpl( "persistence-unit-info", xmlFile )
      );
      xmlDocuments.add( metadataXml );
      try {
        final Element rootElement = metadataXml.getDocumentTree().getRootElement();
        if ( rootElement != null && "entity-mappings".equals( rootElement.getName() ) ) {
          Element element = rootElement.element( "package" );
          String defaultPackage = element != null ? element.getTextTrim() : null;
          List<Element> elements = rootElement.elements( "entity" );
          for (Element subelement : elements ) {
            String classname = XMLContext.buildSafeClassName( subelement.attributeValue( "class" ), defaultPackage );
            if ( ! entities.contains( classname ) ) {
              entities.add( classname );
            }
          }
          elements = rootElement.elements( "mapped-superclass" );
          for (Element subelement : elements ) {
            String classname = XMLContext.buildSafeClassName( subelement.attributeValue( "class" ), defaultPackage );
            if ( ! entities.contains( classname ) ) {
              entities.add( classname );
            }
          }
          elements = rootElement.elements( "embeddable" );
          for (Element subelement : elements ) {
            String classname = XMLContext.buildSafeClassName( subelement.attributeValue( "class" ), defaultPackage );
            if ( ! entities.contains( classname ) ) {
              entities.add( classname );
            }
          }
        }
        else if ( rootElement != null && "hibernate-mappings".equals( rootElement.getName() ) ) {
          //FIXME include hbm xml entities to enhance them but entities is also used to collect annotated entities
        }
      }
      finally {
        try {
          fileInputStream.close();
        }
        catch (IOException ioe) {
          log.warn( "Could not close input stream", ioe );
        }
      }
    }
    xmlFiles.clear();
  }

  private void defineTransactionType(Object overridenTxType, Map workingVars) {
    if ( overridenTxType == null ) {
//      if ( transactionType == null ) {
//        transactionType = PersistenceUnitTransactionType.JTA; //this is the default value
//      }
      //nothing to override
    }
    else if ( overridenTxType instanceof String ) {
      transactionType = PersistenceXmlLoader.getTransactionType( (String) overridenTxType );
    }
    else if ( overridenTxType instanceof PersistenceUnitTransactionType ) {
      transactionType = (PersistenceUnitTransactionType) overridenTxType;
    }
    else {
      throw new PersistenceException( getExceptionHeader() +
          AvailableSettings.TRANSACTION_TYPE + " of the wrong class type"
              + ": " + overridenTxType.getClass()
      );
    }

  }

  public Ejb3Configuration setProperty(String key, String value) {
    cfg.setProperty( key, value );
    return this;
  }

  /**
   * Set ScanningContext detectClasses and detectHbmFiles according to context
   */
  private void setDetectedArtifactsOnScanningContext(ScanningContext context,
                             Properties properties,
                             Map overridenProperties,
                             boolean excludeIfNotOverriden) {

    boolean detectClasses = false;
    boolean detectHbm = false;
    String detectSetting = overridenProperties != null ?
        (String) overridenProperties.get( AvailableSettings.AUTODETECTION ) :
        null;
    detectSetting = detectSetting == null ?
        properties.getProperty( AvailableSettings.AUTODETECTION) :
        detectSetting;
    if ( detectSetting == null && excludeIfNotOverriden) {
      //not overriden through HibernatePersistence.AUTODETECTION so we comply with the spec excludeUnlistedClasses
      context.detectClasses( false ).detectHbmFiles( false );
      return;
    }

    if ( detectSetting == null){
      detectSetting = "class,hbm";
    }
    StringTokenizer st = new StringTokenizer( detectSetting, ", ", false );
    while ( st.hasMoreElements() ) {
      String element = (String) st.nextElement();
      if ( "class".equalsIgnoreCase( element ) ) detectClasses = true;
      if ( "hbm".equalsIgnoreCase( element ) ) detectHbm = true;
    }
    log.debug( "Detect class: {}; detect hbm: {}", detectClasses, detectHbm );
    context.detectClasses( detectClasses ).detectHbmFiles( detectHbm );
  }

  private void scanForClasses(ScanningContext scanningContext, List<String> packages, List<String> entities, List<NamedInputStream> hbmFiles) {
    if (scanningContext.url == null) {
      log.error( "Container is providing a null PersistenceUnitRootUrl: discovery impossible");
      return;
    }
    try {
      addScannedEntries( scanningContext, entities, packages, hbmFiles, null );
    }
    catch (RuntimeException e) {
      throw new RuntimeException( "error trying to scan <jar-file>: " + scanningContext.url.toString(), e );
    }
    catch( IOException e ) {
      throw new RuntimeException( "Error while reading " + scanningContext.url.toString(), e );
    }
  }

  /**
   * create a factory from a list of properties and
   * HibernatePersistence.CLASS_NAMES -> Collection<String> (use to list the classes from config files
   * HibernatePersistence.PACKAGE_NAMES -> Collection<String> (use to list the mappings from config files
   * HibernatePersistence.HBXML_FILES -> Collection<InputStream> (input streams of hbm files)
   * HibernatePersistence.LOADED_CLASSES -> Collection<Class> (list of loaded classes)
   * <p/>
   * <b>Used by JBoss AS only</b>
   * @deprecated use the Java Persistence API
   */
  // This is used directly by JBoss so don't remove until further notice.  bill@jboss.org
  public EntityManagerFactory createEntityManagerFactory(Map workingVars) {
    Properties props = new Properties();
    if ( workingVars != null ) {
      props.putAll( workingVars );
      //remove huge non String elements for a clean props
      props.remove( AvailableSettings.CLASS_NAMES );
      props.remove( AvailableSettings.PACKAGE_NAMES );
      props.remove( AvailableSettings.HBXML_FILES );
      props.remove( AvailableSettings.LOADED_CLASSES );
    }
    configure( props, workingVars );
    return buildEntityManagerFactory();
  }

  /**
   * Process configuration and build an EntityManagerFactory <b>when</b> the configuration is ready
   * @deprecated
   */
  public EntityManagerFactory createEntityManagerFactory() {
    configure( cfg.getProperties(), new HashMap() );
    return buildEntityManagerFactory();
  }

  public EntityManagerFactory buildEntityManagerFactory() {
    Thread thread = null;
    ClassLoader contextClassLoader = null;
    if (overridenClassLoader != null) {
      thread = Thread.currentThread();
      contextClassLoader = thread.getContextClassLoader();
      thread.setContextClassLoader( overridenClassLoader );
    }
    try {
      configure( (Properties)null, null );
      NamingHelper.bind(this);
      return new EntityManagerFactoryImpl(
          cfg.buildSessionFactory(),
          transactionType,
          discardOnClose,
          getSessionInterceptorClass( cfg.getProperties() ),
          cfg
      );
    }
    catch (HibernateException e) {
      throw new PersistenceException( getExceptionHeader() + "Unable to build EntityManagerFactory", e );
    }
    finally {
      if (thread != null) {
        thread.setContextClassLoader( contextClassLoader );
      }
    }
  }

  private Class getSessionInterceptorClass(Properties properties) {
    String sessionInterceptorClassname = (String) properties.get( AvailableSettings.SESSION_INTERCEPTOR );
    if ( StringHelper.isNotEmpty( sessionInterceptorClassname ) ) {
      try {
        Class interceptorClass = ReflectHelper.classForName( sessionInterceptorClassname, Ejb3Configuration.class );
        interceptorClass.newInstance();
        return interceptorClass;
      }
      catch (ClassNotFoundException e) {
        throw new PersistenceException( getExceptionHeader() + "Unable to load "
            + AvailableSettings.SESSION_INTERCEPTOR + ": " + sessionInterceptorClassname, e);
      }
      catch (IllegalAccessException e) {
        throw new PersistenceException( getExceptionHeader() + "Unable to instanciate "
            + AvailableSettings.SESSION_INTERCEPTOR + ": " + sessionInterceptorClassname, e);
      }
      catch (InstantiationException e) {
        throw new PersistenceException( getExceptionHeader() + "Unable to instanciate "
            + AvailableSettings.SESSION_INTERCEPTOR + ": " + sessionInterceptorClassname, e);
      }
    }
    else {
      return null;
    }
  }

  public Reference getReference() throws NamingException {
    log.debug("Returning a Reference to the Ejb3Configuration");
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    ObjectOutput out = null;
    byte[] serialized;
    try {
      out = new ObjectOutputStream( stream );
      out.writeObject( this );
      out.close();
      serialized = stream.toByteArray();
      stream.close();
    }
    catch (IOException e) {
      NamingException namingException = new NamingException( "Unable to serialize Ejb3Configuration" );
      namingException.setRootCause( e );
      throw namingException;
    }

    return new Reference(
        Ejb3Configuration.class.getName(),
        new BinaryRefAddr("object", serialized ),
        Ejb3ConfigurationObjectFactory.class.getName(),
        null
    );
  }

  /**
   * Configures this configuration object from 2 distinctly different sources.
   *
   * @param properties These are the properties that came from the user, either via
   * a persistence.xml or explicitly passed in to one of our
   * {@link javax.persistence.spi.PersistenceProvider}/{@link HibernatePersistence} contracts.
   * @param workingVars Is collection of settings which need to be handled similarly
   * between the 2 main bootstrap methods, but where the values are determine very differently
   * by each bootstrap method.  todo eventually make this a contract (class/interface)
   *
   * @return The configured configuration
   *
   * @see HibernatePersistence
   */
  private Ejb3Configuration configure(Properties properties, Map workingVars) {
    //TODO check for people calling more than once this method (except buildEMF)
    if (isConfigurationProcessed) return this;
    isConfigurationProcessed = true;
    Properties preparedProperties = prepareProperties( properties, workingVars );
    if ( workingVars == null ) workingVars = CollectionHelper.EMPTY_MAP;

    if ( preparedProperties.containsKey( AvailableSettings.CFG_FILE ) ) {
      String cfgFileName = preparedProperties.getProperty( AvailableSettings.CFG_FILE );
      cfg.configure( cfgFileName );
    }

    cfg.addProperties( preparedProperties ); //persistence.xml has priority over hibernate.cfg.xml

    addClassesToSessionFactory( workingVars );

    //processes specific properties
    List<String> jaccKeys = new ArrayList<String>();


    Interceptor defaultInterceptor = DEFAULT_CONFIGURATION.getInterceptor();
    NamingStrategy defaultNamingStrategy = DEFAULT_CONFIGURATION.getNamingStrategy();

    Iterator propertyIt = preparedProperties.keySet().iterator();
    while ( propertyIt.hasNext() ) {
      Object uncastObject = propertyIt.next();
      //had to be safe
      if ( uncastObject != null && uncastObject instanceof String ) {
        String propertyKey = (String) uncastObject;
        if ( propertyKey.startsWith( AvailableSettings.CLASS_CACHE_PREFIX ) ) {
          setCacheStrategy( propertyKey, preparedProperties, true, workingVars );
        }
        else if ( propertyKey.startsWith( AvailableSettings.COLLECTION_CACHE_PREFIX ) ) {
          setCacheStrategy( propertyKey, preparedProperties, false, workingVars );
        }
        else if ( propertyKey.startsWith( AvailableSettings.JACC_PREFIX )
            && ! ( propertyKey.equals( AvailableSettings.JACC_CONTEXT_ID )
            || propertyKey.equals( AvailableSettings.JACC_ENABLED ) ) ) {
          jaccKeys.add( propertyKey );
        }
      }
    }
    final Interceptor interceptor = instantiateCustomClassFromConfiguration(
        preparedProperties,
        defaultInterceptor,
        cfg.getInterceptor(),
        AvailableSettings.INTERCEPTOR,
        "interceptor",
        Interceptor.class
    );
    if ( interceptor != null ) {
      cfg.setInterceptor( interceptor );
    }
    final NamingStrategy namingStrategy = instantiateCustomClassFromConfiguration(
        preparedProperties,
        defaultNamingStrategy,
        cfg.getNamingStrategy(),
        AvailableSettings.NAMING_STRATEGY,
        "naming strategy",
        NamingStrategy.class
    );
    if ( namingStrategy != null ) {
      cfg.setNamingStrategy( namingStrategy );
    }

    final PersisterClassProvider persisterClassProvider = instantiateCustomClassFromConfiguration(
        preparedProperties,
        null,
        cfg.getPersisterClassProvider(),
        AvailableSettings.PERSISTER_CLASS_PROVIDER,
        "persister class provider",
        PersisterClassProvider.class
    );
    if ( persisterClassProvider != null ) {
      cfg.setPersisterClassProvider( persisterClassProvider );
    }

    if ( jaccKeys.size() > 0 ) {
      addSecurity( jaccKeys, preparedProperties, workingVars );
    }

    //initialize listeners
    listenerConfigurator.setProperties( preparedProperties );
    listenerConfigurator.configure();

    //some spec compliance checking
    //TODO centralize that?
    if ( ! "true".equalsIgnoreCase( cfg.getProperty( Environment.AUTOCOMMIT ) ) ) {
      log.warn( "{} = false break the EJB3 specification", Environment.AUTOCOMMIT );
    }
    discardOnClose = preparedProperties.getProperty( AvailableSettings.DISCARD_PC_ON_CLOSE )
        .equals( "true" );
    return this;
  }

  private <T> T instantiateCustomClassFromConfiguration(
      Properties preparedProperties,
      T defaultObject,
      T cfgObject,
      String propertyName,
      String classDescription,
      Class<T> objectClass) {
    if ( preparedProperties.containsKey( propertyName )
        && ( cfgObject == null || cfgObject.equals( defaultObject ) ) ) {
      //cfg.setXxx has precedence over configuration file
      String className = preparedProperties.getProperty( propertyName );
      try {
        Class<T> clazz = (Class<T>) classForName( className );
        return clazz.newInstance();
        //cfg.setInterceptor( (Interceptor) instance.newInstance() );
      }
      catch (ClassNotFoundException e) {
        throw new PersistenceException(
            getExceptionHeader() + "Unable to find " + classDescription + " class: " + className, e
        );
      }
      catch (IllegalAccessException e) {
        throw new PersistenceException(
            getExceptionHeader() + "Unable to access " + classDescription + " class: " + className, e
        );
      }
      catch (InstantiationException e) {
        throw new PersistenceException(
            getExceptionHeader() + "Unable to instantiate " + classDescription + " class: " + className, e
        );
      }
      catch (ClassCastException e) {
        throw new PersistenceException(
            getExceptionHeader() + classDescription + " class does not implement " + objectClass + " interface: "
                + className, e
        );
      }
    }
    return null;
  }

  @SuppressWarnings({ "unchecked" })
  private void addClassesToSessionFactory(Map workingVars) {
    if ( workingVars.containsKey( AvailableSettings.CLASS_NAMES ) ) {
      Collection<String> classNames = (Collection<String>) workingVars.get(
          AvailableSettings.CLASS_NAMES
      );
      addNamedAnnotatedClasses( this, classNames, workingVars );
    }

    if ( workingVars.containsKey( PARSED_MAPPING_DOMS ) ) {
      Collection<XmlDocument> xmlDocuments = (Collection<XmlDocument>) workingVars.get( PARSED_MAPPING_DOMS );
      for ( XmlDocument xmlDocument : xmlDocuments ) {
        cfg.add( xmlDocument );
      }
    }

    //TODO apparently only used for Tests, get rid of it?
    if ( workingVars.containsKey( AvailableSettings.LOADED_CLASSES ) ) {
      Collection<Class> classes = (Collection<Class>) workingVars.get( AvailableSettings.LOADED_CLASSES );
      for ( Class clazz : classes ) {
        cfg.addAnnotatedClass( clazz );
      }
    }
    if ( workingVars.containsKey( AvailableSettings.PACKAGE_NAMES ) ) {
      Collection<String> packages = (Collection<String>) workingVars.get(
          AvailableSettings.PACKAGE_NAMES
      );
      for ( String pkg : packages ) {
        cfg.addPackage( pkg );
      }
    }
    if ( workingVars.containsKey( AvailableSettings.XML_FILE_NAMES ) ) {
      Collection<String> xmlFiles = (Collection<String>) workingVars.get(
          AvailableSettings.XML_FILE_NAMES
      );
      for ( String xmlFile : xmlFiles ) {
        Boolean useMetaInf = null;
        try {
          if ( xmlFile.endsWith( META_INF_ORM_XML ) ) useMetaInf = true;
          cfg.addResource( xmlFile );
        }
        catch( MappingNotFoundException e ) {
          if ( ! xmlFile.endsWith( META_INF_ORM_XML ) ) {
            throw new PersistenceException( getExceptionHeader()
                + "Unable to find XML mapping file in classpath: " + xmlFile);
          }
          else {
            useMetaInf = false;
            //swallow it, the META-INF/orm.xml is optional
          }
        }
        catch( MappingException me ) {
          throw new PersistenceException( getExceptionHeader()
                + "Error while reading JPA XML file: " + xmlFile, me);
        }
        if ( log.isInfoEnabled() ) {
          if ( Boolean.TRUE.equals( useMetaInf ) ) {
            log.info( "{} {} found", getExceptionHeader(), META_INF_ORM_XML);
          }
          else if (Boolean.FALSE.equals( useMetaInf ) ) {
            log.info( "{} No {} found", getExceptionHeader(), META_INF_ORM_XML);
          }
        }
      }
    }
    if ( workingVars.containsKey( AvailableSettings.HBXML_FILES ) ) {
      Collection<NamedInputStream> hbmXmlFiles = (Collection<NamedInputStream>) workingVars.get(
          AvailableSettings.HBXML_FILES
      );
      for ( NamedInputStream is : hbmXmlFiles ) {
        try {
          //addInputStream has the responsibility to close the stream
          cfg.addInputStream( new BufferedInputStream( is.getStream() ) );
        }
        catch (MappingException me) {
          //try our best to give the file name
          if ( StringHelper.isEmpty( is.getName() ) ) {
            throw me;
          }
          else {
            throw new MappingException("Error while parsing file: " + is.getName(), me );
          }
        }
      }
    }
  }

  private String getExceptionHeader() {
    if ( StringHelper.isNotEmpty( persistenceUnitName ) ) {
      return "[PersistenceUnit: " + persistenceUnitName + "] ";
    }
    else {
      return "";
    }
  }

  private Properties prepareProperties(Properties properties, Map workingVars) {
    Properties preparedProperties = new Properties();

    //defaults different from Hibernate
    preparedProperties.setProperty( Environment.RELEASE_CONNECTIONS, "auto" );
    preparedProperties.setProperty( Environment.JPAQL_STRICT_COMPLIANCE, "true" );
    //settings that always apply to a compliant EJB3
    preparedProperties.setProperty( Environment.AUTOCOMMIT, "true" );
    preparedProperties.setProperty( Environment.USE_IDENTIFIER_ROLLBACK, "false" );
    preparedProperties.setProperty( Environment.FLUSH_BEFORE_COMPLETION, "false" );
    preparedProperties.setProperty( AvailableSettings.DISCARD_PC_ON_CLOSE, "false" );
    if (cfgXmlResource != null) {
      preparedProperties.setProperty( AvailableSettings.CFG_FILE, cfgXmlResource );
      cfgXmlResource = null;
    }

    //override the new defaults with the user defined ones
    //copy programmatically defined properties
    if ( cfg.getProperties() != null ) preparedProperties.putAll( cfg.getProperties() );
    //copy them coping from configuration
    if ( properties != null ) preparedProperties.putAll( properties );
    //note we don't copy cfg.xml properties, since they have to be overriden

    if (transactionType == null) {
      //if it has not been set, the user use a programmatic way
      transactionType = PersistenceUnitTransactionType.RESOURCE_LOCAL;
    }
    defineTransactionType(
        preparedProperties.getProperty( AvailableSettings.TRANSACTION_TYPE ),
        workingVars
    );
    boolean hasTxStrategy = StringHelper.isNotEmpty(
        preparedProperties.getProperty( Environment.TRANSACTION_STRATEGY )
    );
    if ( ! hasTxStrategy && transactionType == PersistenceUnitTransactionType.JTA ) {
      preparedProperties.setProperty(
          Environment.TRANSACTION_STRATEGY, JoinableCMTTransactionFactory.class.getName()
      );
    }
    else if ( ! hasTxStrategy && transactionType == PersistenceUnitTransactionType.RESOURCE_LOCAL ) {
      preparedProperties.setProperty( Environment.TRANSACTION_STRATEGY, JDBCTransactionFactory.class.getName() );
    }
    if ( hasTxStrategy ) {
      log.warn(
          "Overriding {} is dangerous, this might break the EJB3 specification implementation",
          Environment.TRANSACTION_STRATEGY
      );
    }
    if ( preparedProperties.getProperty( Environment.FLUSH_BEFORE_COMPLETION ).equals( "true" ) ) {
      preparedProperties.setProperty( Environment.FLUSH_BEFORE_COMPLETION, "false" );
      log.warn( "Defining {}=true ignored in HEM", Environment.FLUSH_BEFORE_COMPLETION );
    }
    return preparedProperties;
  }

  private Class classForName(String className) throws ClassNotFoundException {
    return ReflectHelper.classForName( className, this.getClass() );
  }

  private void setCacheStrategy(String propertyKey, Map properties, boolean isClass, Map workingVars) {
    String role = propertyKey.substring(
        ( isClass ? AvailableSettings.CLASS_CACHE_PREFIX
            .length() : AvailableSettings.COLLECTION_CACHE_PREFIX.length() )
            + 1
    );
    //dot size added
    String value = (String) properties.get( propertyKey );
    StringTokenizer params = new StringTokenizer( value, ";, " );
    if ( !params.hasMoreTokens() ) {
      StringBuilder error = new StringBuilder( "Illegal usage of " );
      error.append(
          isClass ? AvailableSettings.CLASS_CACHE_PREFIX : AvailableSettings.COLLECTION_CACHE_PREFIX
      );
      error.append( ": " ).append( propertyKey ).append( " " ).append( value );
      throw new PersistenceException( getExceptionHeader() + error.toString() );
    }
    String usage = params.nextToken();
    String region = null;
    if ( params.hasMoreTokens() ) {
      region = params.nextToken();
    }
    if ( isClass ) {
      boolean lazyProperty = true;
      if ( params.hasMoreTokens() ) {
        lazyProperty = "all".equalsIgnoreCase( params.nextToken() );
      }
      cfg.setCacheConcurrencyStrategy( role, usage, region, lazyProperty );
    }
    else {
      cfg.setCollectionCacheConcurrencyStrategy( role, usage, region );
    }
  }

  private void addSecurity(List<String> keys, Map properties, Map workingVars) {
    log.debug( "Adding security" );
    if ( !properties.containsKey( AvailableSettings.JACC_CONTEXT_ID ) ) {
      throw new PersistenceException( getExceptionHeader() +
          "Entities have been configured for JACC, but "
              + AvailableSettings.JACC_CONTEXT_ID
              + " has not been set"
      );
    }
    String contextId = (String) properties.get( AvailableSettings.JACC_CONTEXT_ID );
    setProperty( Environment.JACC_CONTEXTID, contextId );

    int roleStart = AvailableSettings.JACC_PREFIX.length() + 1;

    for ( String key : keys ) {
      JACCConfiguration jaccCfg = new JACCConfiguration( contextId );
      try {
        String role = key.substring( roleStart, key.indexOf( '.', roleStart ) );
        int classStart = roleStart + role.length() + 1;
        String clazz = key.substring( classStart, key.length() );
        String actions = (String) properties.get( key );
        jaccCfg.addPermission( role, clazz, actions );
      }
      catch (IndexOutOfBoundsException e) {
        throw new PersistenceException( getExceptionHeader() +
            "Illegal usage of " + AvailableSettings.JACC_PREFIX + ": " + key );
      }
    }
  }

  private void addNamedAnnotatedClasses(
      Ejb3Configuration cfg, Collection<String> classNames, Map workingVars
  ) {
    for ( String name : classNames ) {
      try {
        Class clazz = classForName( name );
        cfg.addAnnotatedClass( clazz );
      }
      catch (ClassNotFoundException cnfe) {
        Package pkg;
        try {
          pkg = classForName( name + ".package-info" ).getPackage();
        }
        catch (ClassNotFoundException e) {
          pkg = null;
        }
        if ( pkg == null ) {
          throw new PersistenceException( getExceptionHeader() "class or package not found", cnfe );
        }
        else {
          cfg.addPackage( name );
        }
      }
    }
  }


  public Settings buildSettings() throws HibernateException {
    Thread thread = null;
    ClassLoader contextClassLoader = null;
    if (overridenClassLoader != null) {
      thread = Thread.currentThread();
      contextClassLoader = thread.getContextClassLoader();
      thread.setContextClassLoader( overridenClassLoader );
    }
    try {
      return settingsFactory.buildSettings( cfg.getProperties() );
    }
    finally {
      if (thread != null) thread.setContextClassLoader( contextClassLoader );
    }
  }

  public Ejb3Configuration addProperties(Properties props) {
    cfg.addProperties( props );
    return this;
  }

  public Ejb3Configuration addAnnotatedClass(Class persistentClass) throws MappingException {
    Thread thread = null;
    ClassLoader contextClassLoader = null;
    if (overridenClassLoader != null) {
      thread = Thread.currentThread();
      contextClassLoader = thread.getContextClassLoader();
      thread.setContextClassLoader( overridenClassLoader );
    }
    try {
      cfg.addAnnotatedClass( persistentClass );
      return this;
    }
    finally {
      if (thread != null) thread.setContextClassLoader( contextClassLoader );
    }
  }

  public Ejb3Configuration configure(String resource) throws HibernateException {
    //delay the call to configure to allow proper addition of all annotated classes (EJB-330)
    if (cfgXmlResource != null)
      throw new PersistenceException("configure(String) method already called for " + cfgXmlResource);
    this.cfgXmlResource = resource;
    return this;
  }

  public Ejb3Configuration addPackage(String packageName) throws MappingException {
    Thread thread = null;
    ClassLoader contextClassLoader = null;
    if (overridenClassLoader != null) {
      thread = Thread.currentThread();
      contextClassLoader = thread.getContextClassLoader();
      thread.setContextClassLoader( overridenClassLoader );
    }
    try {
      cfg.addPackage( packageName );
      return this;
    }
    finally {
      if (thread != null) thread.setContextClassLoader( contextClassLoader );
    }
  }

  public Ejb3Configuration addFile(String xmlFile) throws MappingException {
    Thread thread = null;
    ClassLoader contextClassLoader = null;
    if (overridenClassLoader != null) {
      thread = Thread.currentThread();
      contextClassLoader = thread.getContextClassLoader();
      thread.setContextClassLoader( overridenClassLoader );
    }
    try {
      cfg.addFile( xmlFile );
      return this;
    }
    finally {
      if (thread != null) thread.setContextClassLoader( contextClassLoader );
    }
  }

  public Ejb3Configuration addClass(Class persistentClass) throws MappingException {
    Thread thread = null;
    ClassLoader contextClassLoader = null;
    if (overridenClassLoader != null) {
      thread = Thread.currentThread();
      contextClassLoader = thread.getContextClassLoader();
      thread.setContextClassLoader( overridenClassLoader );
    }
    try {
      cfg.addClass( persistentClass );
      return this;
    }
    finally {
      if (thread != null) thread.setContextClassLoader( contextClassLoader );
    }
  }

  public Ejb3Configuration addFile(File xmlFile) throws MappingException {
    Thread thread = null;
    ClassLoader contextClassLoader = null;
    if (overridenClassLoader != null) {
      thread = Thread.currentThread();
      contextClassLoader = thread.getContextClassLoader();
      thread.setContextClassLoader( overridenClassLoader );
    }
    try {
      cfg.addFile( xmlFile );
      return this;
    }
    finally {
      if (thread != null) thread.setContextClassLoader( contextClassLoader );
    }
  }

  public void buildMappings() {
    Thread thread = null;
    ClassLoader contextClassLoader = null;
    if (overridenClassLoader != null) {
      thread = Thread.currentThread();
      contextClassLoader = thread.getContextClassLoader();
      thread.setContextClassLoader( overridenClassLoader );
    }
    try {
      cfg.buildMappings();
    }
    finally {
      if (thread != null) thread.setContextClassLoader( contextClassLoader );
    }
  }

  public Iterator getClassMappings() {
    Thread thread = null;
    ClassLoader contextClassLoader = null;
    if (overridenClassLoader != null) {
      thread = Thread.currentThread();
      contextClassLoader = thread.getContextClassLoader();
      thread.setContextClassLoader( overridenClassLoader );
    }
    try {
      return cfg.getClassMappings();
    }
    finally {
      if (thread != null) thread.setContextClassLoader( contextClassLoader );
    }
  }

  public EventListeners getEventListeners() {
    return cfg.getEventListeners();
  }

  SessionFactory buildSessionFactory() throws HibernateException {
    return cfg.buildSessionFactory();
  }

  public Iterator getTableMappings() {
    return cfg.getTableMappings();
  }

  public PersistentClass getClassMapping(String persistentClass) {
    return cfg.getClassMapping( persistentClass );
  }

  public org.hibernate.mapping.Collection getCollectionMapping(String role) {
    return cfg.getCollectionMapping( role );
  }

  public void setEntityResolver(EntityResolver entityResolver) {
    cfg.setEntityResolver( entityResolver );
  }

  public Map getNamedQueries() {
    return cfg.getNamedQueries();
  }

  public Interceptor getInterceptor() {
    return cfg.getInterceptor();
  }

  public Properties getProperties() {
    return cfg.getProperties();
  }

  public Ejb3Configuration setInterceptor(Interceptor interceptor) {
    cfg.setInterceptor( interceptor );
    return this;
  }

  public Ejb3Configuration setProperties(Properties properties) {
    cfg.setProperties( properties );
    return this;
  }

  public Map getFilterDefinitions() {
    return cfg.getFilterDefinitions();
  }

  public void addFilterDefinition(FilterDefinition definition) {
    cfg.addFilterDefinition( definition );
  }

  public void addAuxiliaryDatabaseObject(AuxiliaryDatabaseObject object) {
    cfg.addAuxiliaryDatabaseObject( object );
  }

  public NamingStrategy getNamingStrategy() {
    return cfg.getNamingStrategy();
  }

  public Ejb3Configuration setNamingStrategy(NamingStrategy namingStrategy) {
    cfg.setNamingStrategy( namingStrategy );
    return this;
  }

  public Ejb3Configuration setPersisterClassProvider(PersisterClassProvider persisterClassProvider) {
    cfg.setPersisterClassProvider( persisterClassProvider );
    return this;
  }

  public void setListeners(String type, String[] listenerClasses) {
    cfg.setListeners( type, listenerClasses );
  }

  public void setListeners(String type, Object[] listeners) {
    cfg.setListeners( type, listeners );
  }

  /**
   * This API is intended to give a read-only configuration.
   * It is sueful when working with SchemaExport or any Configuration based
   * tool.
   * DO NOT update configuration through it.
   */
  public AnnotationConfiguration getHibernateConfiguration() {
    //TODO make it really read only (maybe through proxying)
    return cfg;
  }

  public Ejb3Configuration addInputStream(InputStream xmlInputStream) throws MappingException {
    Thread thread = null;
    ClassLoader contextClassLoader = null;
    if (overridenClassLoader != null) {
      thread = Thread.currentThread();
      contextClassLoader = thread.getContextClassLoader();
      thread.setContextClassLoader( overridenClassLoader );
    }
    try {
      cfg.addInputStream( xmlInputStream );
      return this;
    }
    finally {
      if (thread != null) thread.setContextClassLoader( contextClassLoader );
    }
  }

  public Ejb3Configuration addResource(String path) throws MappingException {
    Thread thread = null;
    ClassLoader contextClassLoader = null;
    if (overridenClassLoader != null) {
      thread = Thread.currentThread();
      contextClassLoader = thread.getContextClassLoader();
      thread.setContextClassLoader( overridenClassLoader );
    }
    try {
      cfg.addResource( path );
      return this;
    }
    finally {
      if (thread != null) thread.setContextClassLoader( contextClassLoader );
    }
  }

  public Ejb3Configuration addResource(String path, ClassLoader classLoader) throws MappingException {
    cfg.addResource( path, classLoader );
    return this;
  }

  private enum XML_SEARCH {
    HBM,
    ORM_XML,
    BOTH,
    NONE;

    public static XML_SEARCH getType(boolean searchHbm, boolean searchOrm) {
      return searchHbm ?
          searchOrm ? XML_SEARCH.BOTH : XML_SEARCH.HBM :
          searchOrm ? XML_SEARCH.ORM_XML : XML_SEARCH.NONE;
    }
  }
}
TOP

Related Classes of org.hibernate.ejb.Ejb3Configuration

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.