persistence@glassfish.java.net

solution for issue 998?

From: Marina Vatkina <Marina.Vatkina_at_Sun.COM>
Date: Thu, 31 Aug 2006 18:17:39 -0700

Team,

The following solution solves the logger integration problem (all changes are
made to EntityManagerSetupImpl):

1. Modify updateServerPlatform() and move the lines

         if(!session.getLogin().shouldUseExternalTransactionController()) {
             serverPlatform.disableJTA();
         }
to be after updateLogins(m) as there was a comment
"update ServerPlatform must be called after updateLogins - to set correct useJTA
flag"

2. Add updateServerPlatform(predeployProperties) after the second call
to initOrUpdateLogging() in predeploy.
Q2.1: can it be done earlier that that?
Q2.2: Why is this method executed at least 4 times (twice in predeploy()
plus twice via updateServerSession(), which comments say can happen
more than once)?

3. I left call to updateServerPlatform() (modified as in #1) from
updateServerSession() as I don't fully understand the reason of calling it from
there.
Q3.1: Should I just remove it?

Other questions:
a) Why do we have protected methods in this class that are not called by any
other class?

b) What is the purpose of method getServerSession(Map) in EntityManagerSetupImpl
that is not called by any other code? Can I remove it (I commented it out and
clean compile had no complains)?

The changed class is attached (with commented out getServerSession(Map) method).

thanks,
-marina


/*
 * The contents of this file are subject to the terms
 * of the Common Development and Distribution License
 * (the "License"). You may not use this file except
 * in compliance with the License.
 *
 * You can obtain a copy of the license at
 * glassfish/bootstrap/legal/CDDLv1.0.txt or
 * https://glassfish.dev.java.net/public/CDDLv1.0.html.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL
 * HEADER in each file and include the License file at
 * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
 * add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your
 * own identifying information: Portions Copyright [yyyy]
 * [name of copyright owner]
 */
// Copyright (c) 1998, 2006, Oracle. All rights reserved.
package oracle.toplink.essentials.internal.ejb.cmp3;

import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.util.*;
import java.lang.reflect.Constructor;
import java.io.IOException;
import java.io.InputStream;

import javax.persistence.spi.PersistenceUnitInfo;
import javax.persistence.spi.ClassTransformer;
import javax.persistence.PersistenceException;

import oracle.toplink.essentials.config.TopLinkProperties;
import oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider;
import oracle.toplink.essentials.ejb.cmp3.persistence.PersistenceUnitProcessor;
import oracle.toplink.essentials.internal.databaseaccess.DatasourcePlatform;
import oracle.toplink.essentials.internal.ejb.cmp3.base.PropertiesHandler;
import oracle.toplink.essentials.internal.weaving.TransformerFactory;
import oracle.toplink.essentials.jndi.JNDIConnector;
import oracle.toplink.essentials.logging.AbstractSessionLog;
import oracle.toplink.essentials.logging.SessionLog;
import oracle.toplink.essentials.internal.security.PrivilegedAccessHelper;
import oracle.toplink.essentials.internal.security.PrivilegedClassForName;
import oracle.toplink.essentials.internal.sessions.AbstractSession;
import oracle.toplink.essentials.sequencing.Sequence;
import oracle.toplink.essentials.sessions.*;
import oracle.toplink.essentials.threetier.ReadConnectionPool;
import oracle.toplink.essentials.threetier.ServerSession;
import oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataProcessor;
import oracle.toplink.essentials.tools.sessionmanagement.SessionManager;
import oracle.toplink.essentials.descriptors.ClassDescriptor;
import oracle.toplink.essentials.internal.ejb.cmp3.base.CMP3Policy;
import oracle.toplink.essentials.platform.server.CustomServerPlatform;
import oracle.toplink.essentials.platform.server.ServerPlatform;
import oracle.toplink.essentials.exceptions.*;
import oracle.toplink.essentials.internal.helper.EJB30ConversionManager;
import javax.persistence.spi.PersistenceUnitTransactionType;

import oracle.toplink.essentials.internal.ejb.cmp3.jdbc.base.DataSourceImpl;
import oracle.toplink.essentials.tools.sessionconfiguration.DescriptorCustomizer;
import oracle.toplink.essentials.tools.sessionconfiguration.SessionCustomizer;
import oracle.toplink.essentials.internal.security.SecurableObjectHolder;

/**
 * INTERNAL:
 * A TopLink specific implementer of the EntityManagerInitializer interface.
 */
public class EntityManagerSetupImpl {
    protected MetadataProcessor processor = null;
    protected PersistenceUnitInfo persistenceUnitInfo = null;
    protected Map predeployProperties = null;
    // may be positive only in STATE_DEPLOYED
    protected int deploymentCount = 0;
    protected ServerSession session = null;
    protected boolean isInContainerMode = false;
    // indicates whether weaving was used on the first run through predeploy (in STATE_INITIAL)
    protected boolean enableLazyForOneToOne = false;
    protected SecurableObjectHolder securableObjectHolder = new SecurableObjectHolder();
    
        public static final String STATE_INITIAL = "Initial";
        public static final String STATE_PREDEPLOYED = "Predeployed";
        public static final String STATE_DEPLOYED = "Deployed";
        public static final String STATE_UNDEPLOYED = "Undeployed";
    
    protected String state = STATE_INITIAL;

        public static final String ERROR_LOADING_XML_FILE = "error_loading_xml_file";
        public static final String EXCEPTION_LOADING_ENTITY_CLASS = "exception_loading_entity_class";
 
    /**
     * This method can be used to ensure the session represented by emSetupImpl
     * is removed from the SessionManager.
     */
    protected void removeSessionFromGlobalSessionManager() {
        if (session != null){
            if(session.isConnected()) {
                session.logout();
            }
            SessionManager.getManager().getSessions().remove(session.getName());
        }
    }
    
    /**
     * INTERNAL:
     * Return a set of class names for each entity or embeddable found in the
     * list of xml descriptor instance documents to be processed by the
     * MetadataProcessor.
     *
     * @param loader
     * @param persistenceUnitInfo
     * @return
     */
    public Set buildPersistentClassSetFromXMLDocuments(ClassLoader loader, PersistenceUnitInfo persistenceUnitInfo) {
        List<String> mappingFileNames = getORMXMLFileNames(loader);
        HashSet classSet = new HashSet();

        Iterator<String> fileNames = mappingFileNames.iterator();
        InputStream stream = null;
        String fileName = null;
        while (fileNames.hasNext()){
            try{
                fileName = fileNames.next();
                stream = PersistenceUnitProcessor.createInputStreamForFileInPersistenceUnit(fileName, persistenceUnitInfo, loader);
                if (stream != null){
                    classSet.addAll(MetadataProcessor.buildClassSet(stream, fileName, loader));
                }
            } catch (IOException exception){
                handleORMException(PersistenceUnitLoadingException.exceptionLoadingORMXML(fileName, exception), session, fileName);
            } finally {
                try{
                    if (stream != null){
                        stream.close();
                    }
                } catch (IOException e){};
            }
        }
        return classSet;
    }
    
    /**
     * Create a list of the entities that will be deployed. This list is build from the information
     * provided in the PersistenceUnitInfo argument.
     * The list contains Classes specified in the PersistenceUnitInfo's class list and also
     * files that are annotated with @Entity, @Embeddable and @MappedSuperclass in
     * the jar files provided in the persistence info.
     * This list of classes will used by TopLink to build a deployment project and to
     * decide what classes to weave.
     * @param info
     * @param loader
     * @return
     */
      public Collection buildEntityList(PersistenceUnitInfo info, ClassLoader loader) {
          Set<String> classNames = PersistenceUnitProcessor.buildPersistentClassSet(info, loader);
          
          // append the list of entity classes that are defined in the XML descriptor
          classNames.addAll(buildPersistentClassSetFromXMLDocuments(loader, info));

          Vector entityList = new Vector();
          Iterator i = classNames.iterator();
          String className = null;
          while (i.hasNext()){
              try{
                  className = (String)i.next();
                  Class entityClass = loader.loadClass(className);
                  entityList.add(entityClass);
              } catch (ClassNotFoundException exc){
                  AbstractSessionLog.getLog().log(SessionLog.CONFIG, "exception_loading_entity_class", className, exc);
              }
          }
          return entityList;
      }
    
    /**
     * Deploy a persistence session and return an EntityManagerFactory.
     *
     * Deployment takes a session that was partially created in the predeploy call and makes it whole.
     *
     * This means doing any configuration that requires the real class definitions for the entities. In
     * the predeploy phase we were in a stage where we were not let allowed to load the real classes.
     *
     * Deploy could be called several times - but only the first call does the actual deploying -
     * additional calls allow to update session properties (provided the session is not connected) and
     * encrease deploymentCount (which decreased by calls to undeploy method).
     *
     * @param realClassLoader The class loader that was used to load the entity classes. This loader
     * will be maintained for the lifespan of the loaded classes.
     * @param additionalProperties added to predeployProperties for updateServerSession overriding existing properties.
     * In JSE case it allows to alter properties in main (as opposed to preMain where preDeploy is called).
     * @return An EntityManagerFactory to be used by the Container to obtain EntityManagers
     */
    public synchronized ServerSession deploy(ClassLoader realClassLoader, Map additionalProperties) {
        if(state != STATE_PREDEPLOYED && state != STATE_DEPLOYED) {
            throw new PersistenceException(EntityManagerSetupException.cannotDeployWithoutPredeploy(persistenceUnitInfo.getPersistenceUnitName()));
        }
        session.log(SessionLog.FINEST, SessionLog.PROPERTIES, "deploy_begin", new Object[]{getPersistenceUnitInfo().getPersistenceUnitName(), state, deploymentCount});
        try {
            Map deployProperties = EntityManagerFactoryProvider.mergeMaps(additionalProperties, predeployProperties);
            EntityManagerFactoryProvider.translateOldProperties(deployProperties, session);
            
            if(state == STATE_PREDEPLOYED) {
                Collection entities = buildEntityList(persistenceUnitInfo, realClassLoader);
        
                // The project is initially created using class names rather than classes. This call will make the conversion
                session.getProject().convertClassNamesToClasses(realClassLoader);
        
                // listeners and queries require the real classes and are therefore built during deploy using the realClassLoader
                processor.setClassLoader(realClassLoader);
                processor.addEntityListeners();
                processor.addNamedQueries();
                processor = null;
        
                initServerSession(deployProperties);
        
                if (session.getIntegrityChecker().hasErrors()){
                    session.handleException(new IntegrityException(session.getIntegrityChecker()));
                }
        
                session.getDatasourcePlatform().getConversionManager().setLoader(realClassLoader);
            }
            deploymentCount++;
            state = STATE_DEPLOYED;
            try{
                updateServerSession(deployProperties);
                if(!session.isConnected()) {
                    if(isValidationOnly(deployProperties, false)) {
                        session.initializeDescriptors();
                    } else {
                        EntityManagerFactoryProvider.login(session, deployProperties);
                        EntityManagerFactoryProvider.generateDDLFiles(session, deployProperties, !isInContainerMode);
                    }
                }
            } catch (RuntimeException exception) {
                cleanUpSessionManager();
                throw exception;
            }
            return session;
        } catch (oracle.toplink.essentials.exceptions.ValidationException exception) {
            throw new javax.persistence.PersistenceException(exception);
        } finally {
            session.log(SessionLog.FINEST, SessionLog.PROPERTIES, "deploy_end", new Object[]{getPersistenceUnitInfo().getPersistenceUnitName(), state, deploymentCount});
            if(state == STATE_UNDEPLOYED) {
                session = null;
            }
        }
    }


    /**
     * INTERNAL:
     * Adds descriptors plus sequencing info found on the project to the session.
     */
    protected void addProjectToSession(ServerSession session, Project project) {
        DatasourcePlatform sessionPlatform = (DatasourcePlatform)session.getDatasourceLogin().getDatasourcePlatform();
        DatasourcePlatform projectPlatform = (DatasourcePlatform)project.getDatasourceLogin().getDatasourcePlatform();
        if (!sessionPlatform.hasDefaultSequence() && projectPlatform.hasDefaultSequence()) {
            sessionPlatform.setDefaultSequence(projectPlatform.getDefaultSequence());
        }
        if ((sessionPlatform.getSequences() == null) || sessionPlatform.getSequences().isEmpty()) {
            if ((projectPlatform.getSequences() != null) && !projectPlatform.getSequences().isEmpty()) {
                sessionPlatform.setSequences(projectPlatform.getSequences());
            }
        } else {
            if ((projectPlatform.getSequences() != null) && !projectPlatform.getSequences().isEmpty()) {
                Iterator itProjectSequences = projectPlatform.getSequences().values().iterator();
                while (itProjectSequences.hasNext()) {
                    Sequence sequence = (Sequence)itProjectSequences.next();
                    if (!sessionPlatform.getSequences().containsKey(sequence.getName())) {
                        sessionPlatform.addSequence(sequence);
                    }
                }
            }
        }
        session.addDescriptors(project);
    }
    
    /**
     * INTERNAL:
     * Put the given session into the session manager so it can be looked up later
     */
    protected void addSessionToGlobalSessionManager() {
        AbstractSession oldSession = (AbstractSession)SessionManager.getManager().getSessions().get(session.getName());
        if(oldSession != null) {
            throw new PersistenceException(EntityManagerSetupException.attemptedRedeployWithoutClose(session.getName()));
        }
        SessionManager.getManager().addSession(session);
    }

    /**
     * INTERNAL:
     * Assign a CMP3Policy to each descriptor
     */
    protected void assignCMP3Policy() {
        // all descriptors assigned CMP3Policy
        Project project = session.getProject();
        for (Iterator iterator = project.getDescriptors().values().iterator(); iterator.hasNext();){
            //bug:4406101 changed class cast to base class, which is used in projects generated from 904 xml
            ClassDescriptor descriptor = (ClassDescriptor)iterator.next();
            
            if(descriptor.getCMPPolicy() == null) {
                descriptor.setCMPPolicy(new CMP3Policy());
            }
        }
    }

    /**
     * INTERNAL:
     * Build a default TopLink ServerPlatform class for use with this platform.
     */
    protected void updateServerPlatform(Map m) {
        String serverPlatformClassName = (String)PropertiesHandler.getPropertyValueLogDebug(TopLinkProperties.TARGET_SERVER, m, session);
        if (serverPlatformClassName == null) {
            return;
        }

        ServerPlatform serverPlatform = null;
        Class cls = findClassForProperty(serverPlatformClassName, TopLinkProperties.TARGET_SERVER);
        try {
            Constructor constructor = cls.getConstructor(new Class[]{oracle.toplink.essentials.internal.sessions.DatabaseSessionImpl.class});
            serverPlatform = (ServerPlatform)constructor.newInstance(new Object[]{session});
        } catch (Exception ex) {
            if(ExternalTransactionController.class.isAssignableFrom(cls)) {
                CustomServerPlatform customServerPlatform = new CustomServerPlatform(session);
                customServerPlatform.setExternalTransactionControllerClass(cls);
                serverPlatform = customServerPlatform;
            } else {
                throw EntityManagerSetupException.failedToInstantiateServerPlatform(serverPlatformClassName, TopLinkProperties.TARGET_SERVER, ex);
            }
        }
        
        session.setServerPlatform(serverPlatform);

        //Initialize the log for the serverPlatform
        setNewLog(serverPlatform.getServerLog());
    }

    protected void setNewLog(SessionLog newLog) {
        if(session.getSessionLog() == newLog) {
            return;
        }
        
        newLog.setLevel(session.getSessionLog().getLevel());
        newLog.setShouldPrintDate(session.getSessionLog().shouldPrintDate());
        newLog.setShouldPrintThread(session.getSessionLog().shouldPrintThread());
        newLog.setShouldPrintSession(session.getSessionLog().shouldPrintSession());
        newLog.setShouldLogExceptionStackTrace(session.getSessionLog().shouldLogExceptionStackTrace());
        
        session.setSessionLog(newLog);
    }
    
    protected static Class findClass(String className) throws ClassNotFoundException, PrivilegedActionException {
        if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
            return (Class)AccessController.doPrivileged(new PrivilegedClassForName(className));
        } else {
            return oracle.toplink.essentials.internal.security.PrivilegedAccessHelper.getClassForName(className);
        }
    }
    
    protected static Class findClassForProperty(String className, String propertyName) {
        try {
            return findClass(className);
        } catch (PrivilegedActionException exception1) {
            throw EntityManagerSetupException.classNotFoundForProperty(className, propertyName, exception1.getException());
        } catch (ClassNotFoundException exception2) {
            throw EntityManagerSetupException.classNotFoundForProperty(className, propertyName, exception2);
        }
    }
    
    protected void updateDescriptorCacheSettings(Map m) {
        Map typeMap = PropertiesHandler.getPrefixValuesLogDebug(TopLinkProperties.CACHE_TYPE_, m, session);
        Map sizeMap = PropertiesHandler.getPrefixValuesLogDebug(TopLinkProperties.CACHE_SIZE_, m, session);
        Map sharedMap = PropertiesHandler.getPrefixValuesLogDebug(TopLinkProperties.CACHE_SHARED_, m, session);
        if(typeMap.isEmpty() && sizeMap.isEmpty() && sharedMap.isEmpty()) {
            return;
        }

        boolean hasDefault = false;
        
        String defaultTypeName = (String)typeMap.remove(TopLinkProperties.DEFAULT);
        Class defaultType = null;
        if(defaultTypeName != null) {
            defaultType = findClassForProperty(defaultTypeName, TopLinkProperties.CACHE_TYPE_DEFAULT);
            hasDefault = true;
        }
        
        String defaultSizeString = (String)sizeMap.remove(TopLinkProperties.DEFAULT);
        Integer defaultSize = null;
        if(defaultSizeString != null) {
            defaultSize = Integer.parseInt(defaultSizeString);
            hasDefault = true;
        }
        
        String defaultSharedString = (String)sharedMap.remove(TopLinkProperties.CACHE_SHARED_DEFAULT);
        Boolean defaultShared = null;
        if(defaultSharedString != null) {
            defaultShared = Boolean.parseBoolean(defaultSharedString);
            hasDefault = true;
        }
        
        Iterator it = session.getDescriptors().values().iterator();
        while (it.hasNext() && (hasDefault || !typeMap.isEmpty() || !sizeMap.isEmpty() || !sharedMap.isEmpty())) {
            ClassDescriptor descriptor = (ClassDescriptor)it.next();
            
            if(descriptor.isAggregateDescriptor() || descriptor.isAggregateCollectionDescriptor()) {
                continue;
            }
            
            String entityName = descriptor.getAlias();
            String className = descriptor.getJavaClass().getName();
            String name;
            
            Class type = defaultType;
            name = entityName;
            String typeName = (String)typeMap.remove(name);
            if(typeName == null) {
                name = className;
                typeName = (String)typeMap.remove(name);
            }
            if(typeName != null) {
                type = findClassForProperty(typeName, TopLinkProperties.CACHE_TYPE_ + name);
            }
            if(type != null) {
                descriptor.setIdentityMapClass(type);
            }

            Integer size = defaultSize;
            name = entityName;
            String sizeString = (String)sizeMap.remove(name);
            if(sizeString == null) {
                name = className;
                sizeString = (String)sizeMap.remove(name);
            }
            if(sizeString != null) {
                size = Integer.parseInt(sizeString);
            }
            if(size != null) {
                descriptor.setIdentityMapSize(size.intValue());
            }

            Boolean shared = defaultShared;
            name = entityName;
            String sharedString = (String)sharedMap.remove(name);
            if(sharedString == null) {
                name = className;
                sharedString = (String)sharedMap.remove(name);
            }
            if(sharedString != null) {
                shared = Boolean.parseBoolean(sharedString);
            }
            if(shared != null) {
                descriptor.setIsIsolated(!shared.booleanValue());
            }
        }
    }

    /**
     * Perform any steps necessary prior to actual deployment. This includes any steps in the session
     * creation that do not require the real loaded domain classes.
     *
     * @return A transformer (which may be null) that should be plugged into the proper
     * classloader to allow classes to be transformed as they get loaded.
     * @see predeploy(Collection entities, ClassLoader privateClassLoader, PersistenceUnitInfo info)
     */
    public ClassTransformer predeploy(PersistenceUnitInfo info, Map extendedProperties) {
        if(state == STATE_INITIAL) {
            persistenceUnitInfo = info;
        }
        ClassLoader privateClassLoader = persistenceUnitInfo.getNewTempClassLoader();
        predeployProperties = EntityManagerFactoryProvider.mergeMaps(extendedProperties, persistenceUnitInfo.getProperties());

        //Bug5389828. Update the logging settings for the singleton logger.
        initOrUpdateLogging(true, predeployProperties, AbstractSessionLog.getLog());
        // build a list of entities the persistence unit represented by this EntityManagerSetupImpl will use
        Collection entities = buildEntityList(persistenceUnitInfo, privateClassLoader);

        session = new ServerSession(new Project(new DatabaseLogin()));
        EntityManagerFactoryProvider.translateOldProperties(predeployProperties, session);
        initOrUpdateLogging(true, predeployProperties, session.getSessionLog());

        updateServerPlatform(predeployProperties);

        session.getPlatform().setConversionManager(new EJB30ConversionManager());
    
        if(!isValidationOnly(predeployProperties, false) && persistenceUnitInfo != null && persistenceUnitInfo.getTransactionType() == PersistenceUnitTransactionType.JTA) {
            if(persistenceUnitInfo.getJtaDataSource() == null) {
                throw new PersistenceException(EntityManagerSetupException.jtaPersistenceUnitInfoMissingJtaDataSource(persistenceUnitInfo.getPersistenceUnitName()));
            }
        }
        
        // this flag is used to disable work done as a result of the LAZY hint on OneToOne mappings
        if(state == STATE_INITIAL ) {
            enableLazyForOneToOne = true;
            String weaving = getConfigPropertyAsString(TopLinkProperties.WEAVING);
            if (weaving != null && weaving.equalsIgnoreCase("false")) {
                enableLazyForOneToOne = false;
            }
        }

        // Process the Object/relational metadata from XML and annotations.
        processORMetadata(privateClassLoader, session, entities, enableLazyForOneToOne);

        // The connector will be reconstructed when the session is actually deployed
        session.getProject().getLogin().setConnector(new DefaultConnector());

        if (session.getIntegrityChecker().hasErrors()){
            session.handleException(new IntegrityException(session.getIntegrityChecker()));
        }

        // The transformer is capable of altering domain classes to handle a LAZY hint for OneToOne mappings. It will only
        // be returned if we we are mean to process these mappings
        ClassTransformer transformer = null;
        if (enableLazyForOneToOne){
            transformer = TransformerFactory.createTransformerAndModifyProject(session, entities, privateClassLoader);
        }
        
        state = STATE_PREDEPLOYED;
        return transformer;
    }

    /**
     * INTERNAL:
     * In case the session is not connected applies to it properties m.
    public ServerSession getServerSession(Map m) {
        updateServerSession(m);
        return session;
    }
     */

      /**
   * Check the provided map for an object with the given key. If that object is not available, check the
   * System properties. If it is not available from either location, return the default value.
   * @param propertyKey
   * @param map
   * @param defaultValue
   * @return
   */
    public String getConfigPropertyAsString(String propertyKey, String defaultValue){
        return EntityManagerFactoryProvider.getConfigPropertyAsStringLogDebug(propertyKey, predeployProperties, defaultValue, session);
    }

    public String getConfigPropertyAsString(String propertyKey){
        return EntityManagerFactoryProvider.getConfigPropertyAsStringLogDebug(propertyKey, predeployProperties, session);
    }

    /**
     * Return the name of the session this SetupImpl is building. The session name is only known at deploy
     * time and if this method is called prior to that, this method will return null.
     * @return
     */
    public String getDeployedSessionName(){
        return session != null ? session.getName() : null;
    }
    
    public PersistenceUnitInfo getPersistenceUnitInfo(){
        return persistenceUnitInfo;
    }
    
    public boolean isValidationOnly(Map m) {
        return isValidationOnly(m, true);
    }
    
    protected boolean isValidationOnly(Map m, boolean shouldMergeMap) {
        if(shouldMergeMap) {
            m = mergeWithExistingMap(m);
        }
        String validationOnlyString = EntityManagerFactoryProvider.getConfigPropertyAsStringLogDebug(EntityManagerFactoryProvider.TOPLINK_VALIDATION_ONLY_PROPERTY, m, session);
        if(validationOnlyString != null) {
            return Boolean.parseBoolean(validationOnlyString);
        } else {
            return false;
        }
    }
    
    public boolean shouldGetSessionOnCreateFactory(Map m) {
        m = mergeWithExistingMap(m);
        return isValidationOnly(m, false);
    }
    
    protected Map mergeWithExistingMap(Map m) {
        if(predeployProperties != null) {
            return EntityManagerFactoryProvider.mergeMaps(m, predeployProperties);
        } else if(persistenceUnitInfo != null) {
            return EntityManagerFactoryProvider.mergeMaps(m, persistenceUnitInfo.getProperties());
        } else {
            return m;
        }
    }

    public boolean isInContainerMode(){
        return isInContainerMode;
    }

    /**
     * Handle an exception that occured while processing ORM xml
     */
    protected void handleORMException(RuntimeException e, AbstractSession session, String mappingFileResourceName){
        String throwXMLExceptions = getConfigPropertyAsString(EntityManagerFactoryProvider.TOPLINK_ORM_THROW_EXCEPTIONS, "true");
        // if fail quietly
        if (throwXMLExceptions == null || throwXMLExceptions.equalsIgnoreCase("false")) {
            session.log(SessionLog.CONFIG, SessionLog.EJB_ORM, ERROR_LOADING_XML_FILE, new Object[] {mappingFileResourceName, e});
        } else {
            // fail loudly
            session.handleException(e);
        }
    }
   
    private List<String> getORMXMLFileNames(ClassLoader loader){
        ArrayList<String> list = new ArrayList<String>();
        String ormXMLFile = "META-INF/orm.xml";
        InputStream stream = null;
        try {
            stream = PersistenceUnitProcessor.createInputStreamForFileInPersistenceUnit(ormXMLFile, persistenceUnitInfo, loader);
            if (stream != null){
                list.add(ormXMLFile);
            }
        } catch (IOException e){
        } finally {
            try{
                if (stream != null){
                    stream.close();
                }
            } catch (IOException e) {}
        }
        
        if (persistenceUnitInfo != null) {
            if (persistenceUnitInfo.getMappingFileNames() != null) {
                Iterator mappingFiles = persistenceUnitInfo.getMappingFileNames().iterator();{
                    while (mappingFiles.hasNext()){
                        list.add((String)mappingFiles.next());
                    }
                }
            }
         }
        return list;
    }
    

  /**
   * Override the default login creation method.
   * If persistenceInfo is available, use the information from it to setup the login
   * and possibly to set readConnectionPool.
   * @param ss
   * @param m
   */
    protected void updateLogins(Map m){
        DatasourceLogin login = session.getLogin();
    
        // Note: This call does not checked the stored persistenceUnitInfo or extended properties because
        // the map passed into this method should represent the full set of properties we expect to process

        String user = EntityManagerFactoryProvider.getConfigPropertyAsStringLogDebug(TopLinkProperties.JDBC_USER, m, session);
        String password = EntityManagerFactoryProvider.getConfigPropertyAsStringLogDebug(TopLinkProperties.JDBC_PASSWORD, m, session);
        if(user != null) {
            login.setUserName(user);
        }
        if(password != null) {
            login.setPassword(securableObjectHolder.getSecurableObject().decryptPassword(password));
        }

        String toplinkPlatform = (String)PropertiesHandler.getPropertyValueLogDebug(TopLinkProperties.TARGET_DATABASE, m, session);
        if (toplinkPlatform != null) {
            login.setPlatformClassName(toplinkPlatform);
        }

        if (isValidationOnly(m, false) && persistenceUnitInfo.getTransactionType() == PersistenceUnitTransactionType.JTA && persistenceUnitInfo.getJtaDataSource() == null){
            updateLoginDefaultConnector(login, m);
            return;
        }
        
        login.setUsesExternalTransactionController(persistenceUnitInfo.getTransactionType() == PersistenceUnitTransactionType.JTA);

        javax.sql.DataSource mainDatasource = null;
        javax.sql.DataSource readDatasource = null;
        if(login.shouldUseExternalTransactionController()) {
            // JtaDataSource is guaranteed to be non null - otherwise exception would've been thrown earlier
            mainDatasource = persistenceUnitInfo.getJtaDataSource();
            // only define readDatasource if there is jta mainDatasource
            readDatasource = persistenceUnitInfo.getNonJtaDataSource();
        } else {
            // JtaDataSource will be ignored because transactionType is RESOURCE_LOCAL
            if(persistenceUnitInfo.getJtaDataSource() != null) {
                session.log(SessionLog.WARNING, SessionLog.TRANSACTION, "resource_local_persistence_init_info_ignores_jta_data_source", persistenceUnitInfo.getPersistenceUnitName());
            }
            if(persistenceUnitInfo.getNonJtaDataSource() != null) {
                mainDatasource = persistenceUnitInfo.getNonJtaDataSource();
            } else {
                updateLoginDefaultConnector(login, m);
                return;
            }
        }

        // mainDatasource is guaranteed to be non null
        if(!(login.getConnector() instanceof JNDIConnector)) {
             JNDIConnector jndiConnector;
            if (mainDatasource instanceof DataSourceImpl) {
                //Bug5209363 Pass in the datasource name instead of the dummy datasource
                jndiConnector = new JNDIConnector(((DataSourceImpl)mainDatasource).getName());
            } else {
                jndiConnector = new JNDIConnector(mainDatasource);
            }
            login.setConnector(jndiConnector);
            login.setUsesExternalConnectionPooling(true);
        }

        // set readLogin
        if(readDatasource != null) {
            DatasourceLogin readLogin = (DatasourceLogin)login.clone();
            readLogin.dontUseExternalTransactionController();
            JNDIConnector jndiConnector;
            if (readDatasource instanceof DataSourceImpl) {
                //Bug5209363 Pass in the datasource name instead of the dummy datasource
                jndiConnector = new JNDIConnector(((DataSourceImpl)readDatasource).getName());
            } else {
                jndiConnector = new JNDIConnector(readDatasource);
            }
            readLogin.setConnector(jndiConnector);
            session.setReadConnectionPool(readLogin);
        }
        
    }

  /**
   * In cases where there is no data source, we will use properties to configure the login for
   * our session. This method gets those properties and sets them on the login.
   * @param login
   * @param m
   */
    protected void updateLoginDefaultConnector(DatasourceLogin login, Map m){
        if((login.getConnector() instanceof DefaultConnector)) {
            DatabaseLogin dbLogin = (DatabaseLogin)login;
            // Note: This call does not checked the stored persistenceUnitInfo or extended properties because
            // the map passed into this method should represent the full set of properties we expect to process
            String jdbcDriver = EntityManagerFactoryProvider.getConfigPropertyAsStringLogDebug(TopLinkProperties.JDBC_DRIVER, m, session);
            String connectionString = EntityManagerFactoryProvider.getConfigPropertyAsStringLogDebug(TopLinkProperties.JDBC_URL, m, session);
            if(connectionString != null) {
                dbLogin.setConnectionString(connectionString);
            }
            if(jdbcDriver != null) {
                dbLogin.setDriverClassName(jdbcDriver);
            }
        }
    }

    protected void updatePools(Map m) {
        // Sizes are irrelevant for external connection pool
        if(!session.getDefaultConnectionPool().getLogin().shouldUseExternalConnectionPooling()) {
            String strWriteMin = EntityManagerFactoryProvider.getConfigPropertyAsStringLogDebug(TopLinkProperties.JDBC_WRITE_CONNECTIONS_MIN, m, session);
            if(strWriteMin != null) {
                session.getDefaultConnectionPool().setMinNumberOfConnections(Integer.parseInt(strWriteMin));
            }
            String strWriteMax = EntityManagerFactoryProvider.getConfigPropertyAsStringLogDebug(TopLinkProperties.JDBC_WRITE_CONNECTIONS_MAX, m, session);
            if(strWriteMax != null) {
                session.getDefaultConnectionPool().setMaxNumberOfConnections(Integer.parseInt(strWriteMax));
            }
        }
        
        // Sizes and shared option are irrelevant for external connection pool
        if(!session.getReadConnectionPool().getLogin().shouldUseExternalConnectionPooling()) {
            String strReadMin = EntityManagerFactoryProvider.getConfigPropertyAsStringLogDebug(TopLinkProperties.JDBC_READ_CONNECTIONS_MIN, m, session);
            if(strReadMin != null) {
                session.getReadConnectionPool().setMinNumberOfConnections(Integer.parseInt(strReadMin));
            }
            String strReadMax = EntityManagerFactoryProvider.getConfigPropertyAsStringLogDebug(TopLinkProperties.JDBC_READ_CONNECTIONS_MAX, m, session);
            if(strReadMax != null) {
                session.getReadConnectionPool().setMaxNumberOfConnections(Integer.parseInt(strReadMax));
            }
            String strShouldUseShared = EntityManagerFactoryProvider.getConfigPropertyAsStringLogDebug(TopLinkProperties.JDBC_READ_CONNECTIONS_SHARED, m,session);
            if(strShouldUseShared != null) {
                boolean shouldUseShared = Boolean.parseBoolean(strShouldUseShared);
                boolean sessionUsesShared = session.getReadConnectionPool() instanceof ReadConnectionPool;
                if(shouldUseShared != sessionUsesShared) {
                    Login readLogin = session.getReadConnectionPool().getLogin();
                    int nReadMin = session.getReadConnectionPool().getMinNumberOfConnections();
                    int nReadMax = session.getReadConnectionPool().getMaxNumberOfConnections();
                    if(shouldUseShared) {
                        session.useReadConnectionPool(nReadMin, nReadMax);
                    } else {
                        session.useExclusiveReadConnectionPool(nReadMin, nReadMax);
                    }
                    // keep original readLogin
                    session.getReadConnectionPool().setLogin(readLogin);
                }
            }
        }
    }
    
  /**
   * Normally when a property is missing nothing should be applied to the session.
   * However there are several session attributes that defaulted in EJB3 to the values
   * different from TopLink defaults (for instance, in TopLink defaults binding to false,
   * EJB3 - to true).
   * This function applies defaults for such properties.
   * All other session-related properties are applied in updateServerSession.
   * Note that updateServerSession may be called several times on the same session
   * (before login), but initServerSession is called just once - before the first call
   * to updateServerSession.
   * @param ss
   */
    protected void initServerSession(Map m) {
        assignCMP3Policy();

        // use default session name if none is provided
        String name = EntityManagerFactoryProvider.getConfigPropertyAsString(TopLinkProperties.SESSION_NAME, m);
        if(name == null) {
            if (persistenceUnitInfo.getPersistenceUnitRootUrl() != null){
                name = persistenceUnitInfo.getPersistenceUnitRootUrl().toString() + "-" + persistenceUnitInfo.getPersistenceUnitName();
            } else {
                name = persistenceUnitInfo.getPersistenceUnitName();
            }
            session.setName(name);
// session.log(SessionLog.FINEST, SessionLog.PROPERTIES, "property_value_default", new Object[]{TopLinkProperties.SESSION_NAME, name});
            addSessionToGlobalSessionManager();
        }
        
        // shouldBindAllParameters is true by default - set it if no property provided
        if (EntityManagerFactoryProvider.getConfigPropertyAsString(TopLinkProperties.JDBC_BIND_PARAMETERS, m) == null) {
// session.log(SessionLog.FINEST, SessionLog.PROPERTIES, "property_value_default", new Object[]{TopLinkProperties.JDBC_BIND_PARAMETERS, "true"});
            session.getPlatform().setShouldBindAllParameters(true);
        }
        
        // set default descriptor cache size - set it to all descriptors if CACHE_SIZE_DEFAULT not provided
        if (PropertiesHandler.getPrefixedPropertyValue(TopLinkProperties.CACHE_SIZE_, TopLinkProperties.DEFAULT, m) == null) {
// int defaultCacheSize = Integer.parseInt(PropertiesHandler.getDefaultPropertyValueLogDebug(TopLinkProperties.CACHE_SIZE_, session));
            int defaultCacheSize = Integer.parseInt(PropertiesHandler.getDefaultPropertyValue(TopLinkProperties.CACHE_SIZE_));
            Iterator descriptorsIterator = session.getDescriptors().values().iterator();
            while (descriptorsIterator.hasNext()) {
                 ((ClassDescriptor)descriptorsIterator.next()).setIdentityMapSize(defaultCacheSize);
            }
        }
    }


  /**
   * Make any changes to our ServerSession that can be made after it is created.
   * @param ss
   * @param m
   */
    protected void updateServerSession(Map m) {
        if (session == null || session.isConnected()) {
            return;
        }

        initOrUpdateLogging(false, m, AbstractSessionLog.getLog());
        initOrUpdateLogging(false, m, session.getSessionLog());

        updateSessionName(m);
        String shouldBindString = EntityManagerFactoryProvider.getConfigPropertyAsStringLogDebug(TopLinkProperties.JDBC_BIND_PARAMETERS, m, session);
        if (shouldBindString != null) {
            session.getPlatform().setShouldBindAllParameters(Boolean.parseBoolean(shouldBindString));
        }

        updateLogins(m);
        if(!session.getLogin().shouldUseExternalTransactionController()) {
            session.getServerPlatform().disableJTA();
        }
        
        updateServerPlatform(m);

        updatePools(m);
        
        updateDescriptorCacheSettings(m);

        // Customizers should be processed last
        processDescriptorCustomizers(m);
        processSessionCustomizer(m);
    }

  /**
   * This sets the isInContainerMode flag.
   * "true" indicates container case, "false" - SE.
   * @param isInContainerMode
   */
    public void setIsInContainerMode(boolean isInContainerMode) {
        this.isInContainerMode = isInContainerMode;
   }

   protected void processSessionCustomizer(Map m) {
        String sessionCustomizerClassName = EntityManagerFactoryProvider.getConfigPropertyAsStringLogDebug(TopLinkProperties.SESSION_CUSTOMIZER, m, session);
        if(sessionCustomizerClassName == null) {
            return;
        }
        
        Class sessionCustomizerClass = findClassForProperty(sessionCustomizerClassName, TopLinkProperties.SESSION_CUSTOMIZER);
        SessionCustomizer sessionCustomizer;
        try {
            sessionCustomizer = (SessionCustomizer)sessionCustomizerClass.newInstance();
            sessionCustomizer.customize(session);
        } catch (Exception ex) {
            throw EntityManagerSetupException.failedWhileProcessingProperty(TopLinkProperties.SESSION_CUSTOMIZER, sessionCustomizerClassName, ex);
        }
   }

    protected void initOrUpdateLogging(boolean init, Map m, SessionLog log) {
        String logLevelString = PropertiesHandler.getPropertyValueLogDebug(TopLinkProperties.LOGGING_LEVEL, m, session);
        if(logLevelString == null && init) {
            logLevelString = PropertiesHandler.getDefaultPropertyValueLogDebug(TopLinkProperties.LOGGING_LEVEL, session);
        }
        if (logLevelString != null) {
            log.setLevel(AbstractSessionLog.translateStringToLoggingLevel(logLevelString));
        }
        String tsString = EntityManagerFactoryProvider.getConfigPropertyAsStringLogDebug(TopLinkProperties.LOGGING_TIMESTAMP, m, session);
        if (tsString != null) {
            log.setShouldPrintDate(Boolean.parseBoolean(tsString));
        }
        String threadString = EntityManagerFactoryProvider.getConfigPropertyAsStringLogDebug(TopLinkProperties.LOGGING_THREAD, m, session);
        if (threadString != null) {
            log.setShouldPrintThread(Boolean.parseBoolean(threadString));
        }
        String sessionString = EntityManagerFactoryProvider.getConfigPropertyAsStringLogDebug(TopLinkProperties.LOGGING_SESSION, m, session);
        if (sessionString != null) {
            log.setShouldPrintSession(Boolean.parseBoolean(sessionString));
        }
        String exString = EntityManagerFactoryProvider.getConfigPropertyAsStringLogDebug(TopLinkProperties.LOGGING_EXCEPTIONS, m, session);
        if (exString != null) {
            log.setShouldLogExceptionStackTrace(Boolean.parseBoolean(exString));
        }
    }

    protected void updateSessionName(Map m) {
        String newName = EntityManagerFactoryProvider.getConfigPropertyAsStringLogDebug(TopLinkProperties.SESSION_NAME, m, session);
        if(newName == null || newName.equals(session.getName())) {
            return;
        }

        removeSessionFromGlobalSessionManager();
        session.setName(newName);
        addSessionToGlobalSessionManager();
    }
    
    protected void processDescriptorCustomizers(Map m) {
        Map customizerMap = PropertiesHandler.getPrefixValuesLogDebug(TopLinkProperties.DESCRIPTOR_CUSTOMIZER_, m, session);
        if(customizerMap.isEmpty()) {
            return;
        }

        Iterator it = customizerMap.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String name = (String)entry.getKey();
            
            ClassDescriptor descriptor = session.getDescriptorForAlias(name);
            if(descriptor == null) {
                try {
                    Class javaClass = findClass(name);
                    descriptor = session.getDescriptor(javaClass);
                } catch (Exception ex) {
                    // Ignore exception
                }
            }
            if(descriptor != null) {
                String customizerClassName = (String)entry.getValue();
                Class customizerClass = findClassForProperty(customizerClassName, TopLinkProperties.DESCRIPTOR_CUSTOMIZER_ + name);
                try {
                    DescriptorCustomizer customizer = (DescriptorCustomizer)customizerClass.newInstance();
                    customizer.customize(descriptor);
                } catch (Exception ex) {
                    throw EntityManagerSetupException.failedWhileProcessingProperty(TopLinkProperties.DESCRIPTOR_CUSTOMIZER_ + name, customizerClassName, ex);
                }
            }
        }
    }
    
    private void processORMetadata(ClassLoader privateClassLoader, AbstractSession session, Collection entities, boolean enableLazyForOneToOne){
        processor = new MetadataProcessor(session, privateClassLoader, enableLazyForOneToOne);
       
        List<String> mappingFileNames = getORMXMLFileNames(privateClassLoader);

        // process persistence unit metadata/defaults defined in ORM XML instance documents in the persistence unit
        processor.processPersistenceUnitMetadata(session, persistenceUnitInfo, mappingFileNames, entities);
        
        Iterator<String> fileNames = mappingFileNames.iterator();
        InputStream inputStream = null;
        String fileName = null;
        while (fileNames.hasNext()){
            try{
                fileName = fileNames.next();
                inputStream = PersistenceUnitProcessor.createInputStreamForFileInPersistenceUnit(fileName, persistenceUnitInfo, privateClassLoader);
                if (inputStream != null){
                    processor.processXML(inputStream, fileName);
                }
            } catch (RuntimeException e){
                handleORMException(e, session, fileName);
            } catch (IOException exc){
                handleORMException(PersistenceUnitLoadingException.exceptionLoadingORMXML(fileName, exc), session, fileName);
            } finally {
                try{
                    if (inputStream != null){
                        inputStream.close();
                    }
                } catch (IOException exc){}
            }
        }
        
        session = (AbstractSession) processor.processAnnotations();
    }
    
    public boolean isPredeployed() {
        return state == STATE_PREDEPLOYED;
    }

    public boolean isDeployed() {
        return state == STATE_DEPLOYED;
    }

    public boolean isUndeployed() {
        return state == STATE_UNDEPLOYED;
    }

    protected void cleanUpSessionManager() {
        deploymentCount--;
        if(deploymentCount > 0) {
            return;
        }
        state = STATE_UNDEPLOYED;
        removeSessionFromGlobalSessionManager();
    }
    
    /**
     * Undeploy may be called several times, but only the call that decreases
     * deploymentCount to 0 disconnects the session and removes it from the session manager.
     * Note that the session is an attribute of this class,
     * and could be deployed again (after been undeployed to deploymentCount 0 and disconnected).
     */
    public synchronized void undeploy() {
        if(state != STATE_DEPLOYED) {
            return;
        }
        session.log(SessionLog.FINEST, SessionLog.PROPERTIES, "undeploy_begin", new Object[]{getPersistenceUnitInfo().getPersistenceUnitName(), state, deploymentCount});
        try {
            cleanUpSessionManager();
        } finally {
            session.log(SessionLog.FINEST, SessionLog.PROPERTIES, "undeploy_end", new Object[]{getPersistenceUnitInfo().getPersistenceUnitName(), state, deploymentCount});
            if(state == STATE_UNDEPLOYED) {
                session = null;
            }
        }
    }
}