persistence@glassfish.java.net

Re: A Question about code in EntityMangerSetupImpl

From: Mitesh Meswani <Mitesh.Meswani_at_Sun.COM>
Date: Wed, 30 May 2007 12:22:39 -0700

Hi Andrei,

Based on your suggestion below, attached are modified files I am
planning to checkin. Please review.

I verified that with these changes the issue with appclient is solved.
There was no regression while running entity-persistence tests. I will
run our internal ejb dev tests. Do you have any other tests that we
should run before checking in the changes?

Thanks,
Mitesh

Andrei Ilitchev wrote:
> Hi Mitesh,
>
> You are right, the classloader passed to updateServerPlatform and
> updateLoggers methods by predeploy should be changed.
>
> The "real" classloader should be obtained from PersistenceUnitInfo:
>
> ClassLoader realClassLoader =
> persistenceUnitInfo.getClassLoader();
> updateServerPlatform(predeployProperties, realClassLoader);
> // Update loggers and settings for the singleton logger
> and the session logger.
> updateLoggers(predeployProperties, true, false,
> realClassLoader);
>
> Additional change will be required for JSE case in
> JavaSECMPInitializer class to ensure that real classloader is set
> before predeploy is called:
> in callPredeploy method the line:
> persistenceUnitInfo.setClassLoader(getMainLoader());
> should be moved from its current location (after emSetupImpl.predeploy
> is called) to the new one (before emSetupImpl.predeploy is called):
> ClassLoader tempLoader = createTempLoader(tempLoaderSet);
> persistenceUnitInfo.setNewTempClassLoader(tempLoader);
> // new location
> persistenceUnitInfo.setClassLoader(getMainLoader());
> Thanks,
>
> Andrei
>
>
> ----- Original Message -----
> *From:* Mitesh Meswani <mailto:Mitesh.Meswani_at_Sun.COM>
> *To:* persistence_at_glassfish.dev.java.net
> <mailto:persistence_at_glassfish.dev.java.net>
> *Cc:* Timothy.Quinn_at_Sun.COM <mailto:Timothy.Quinn_at_Sun.COM>
> *Sent:* Tuesday, May 29, 2007 6:46 PM
> *Subject:* Re: A Question about code in EntityMangerSetupImpl
>
> Hi Andrei,
>
> Thanks for the explanation. Please see inline for more comments.
>
> Andrei Ilitchev wrote:
>> Hi Mitesh,
>>
>> The reason updateServerPlatform method uses loader parameter is
>> that the server platform class may be defined in the persistence
>> unit, and therefore the "main" classloader used to load TopLink
>> classes may not define this class.
>> However the classes defined in the persistence unit should be
>> able to "see" TopLink classes defined by the "main" classloader
>> (the most simple implementation would be the passed class loader
>> is a child of the "main" classloader).
>>
>> Therefore I don't see how the problem may arise when
>> updateServerPlatform is called by deploy method with the "real"
>> classloader.
>>
>> However there may be a problem when the method is called earlier,
>> by predeploy method using temporary classloader (which is also
>> supposed to see TopLink classes):
> If toplink-e classes are loaded by a classloader higher up in
> hierarchy than the classloader that loaded the persistence unit,
> there is no issue. But, if toplink-e classes are loaded by the
> same classloader that loaded the persistence unit, the code
> breaks. This is the case when toplink is used from within
> appclient of glassfish. To give you some more details - The
> classloader hierarchy for the appclient is as follows
>
> SystemClassLoader (Has JDK and couple of appserver jars in
> classpath)
> |
> |
> ApplicationClassLoader (an instance of EJBClassLoader and
> has the client.jar, derby.jar and *toplink-e.jar* in classpath)
>
> Please note that it is expected that persistence provider jars and
> jdbc driver jars can be provided by the user. Hence it is the
> ApplicationClassLoader that loads toplink-e.jar.
> Now, during predeploy,
> EntityMangerSetupImpl#updateServerPlatform(Map m, ClassLoader
> loader) is called with loader set to the tempClassLoader, which is
> a clone of ApplicationClassLoader. When following code is executed
> with this loader,
> Class cls = findClassForProperty(serverPlatformClassName,
> TopLinkProperties.TARGET_SERVER, loader);
> cls is loaded by the tempClassLoader and is unusable because we
> try to find a constructor for this cls that takes
> 'oracle.toplink.essentials.internal.sessions.DatabaseSessionImpl.class'
> loaded by the original ApplicationClassLoader as a parameter.
>
> Is it acceptable to use ContextClassLoader at this point? I
> quickly hacked appclient container code to set correct context
> class loader and it seems to solve the issue. Following is the
> change that I am proposing.
>
> $ cvs -q diff -u
> entity-persistence/src/java/oracle/toplink/essentials/internal/ejb/cmp3/EntityManagerSetupImpl.java
>
> diff -u -r1.54 EntityManagerSetupImpl.java
> ---
> entity-persistence/src/java/oracle/toplink/essentials/internal/ejb/cmp3/EntityManagerSetupImpl.java
> 24 May 2007 17:25:48 -0000 1.54
> +++
> entity-persistence/src/java/oracle/toplink/essentials/internal/ejb/cmp3/EntityManagerSetupImpl.java
> 29 May 2007 22:14:36 -0000
> @@ -319,7 +319,7 @@
> // the new serverPlatform
> ServerPlatform serverPlatform = null;
> // New platform - create the new instance and set it.
> - Class cls = findClassForProperty(serverPlatformClassName,
> TopLinkProperties.TARGET_SERVER, loader);
> + Class cls = findClassForProperty(serverPlatformClassName,
> TopLinkProperties.TARGET_SERVER,
> Thread.currentThread().getContextClassLoader());
> try {
> Constructor constructor = cls.getConstructor(new
> Class[]{oracle.toplink.essentials.internal.sessions.DatabaseSessionImpl.class});
>
> serverPlatform =
> (ServerPlatform)constructor.newInstance(new Object[]{session});
>
>
> Thanks,
> Mitesh
>
>> the server platform instantiated using temporary classloader will
>> be set - and never overridden with the same one loaded using the
>> "real" classloader. Again, that only applicable in case of
>> user-defined server platform. The same applies also to updateLoggers.
>>
>> The only reason to have updateServerPlatform and updateLoggers in
>> predeploy is to make sure that the correct logger is used in
>> predeploy method.
>>
>> updateServerPlatform and updateLoggers methods called for the
>> second time in deploy method (through updateServerSession
>> method): at this point if server platform class name is the same
>> as the original one then the new server platform is not created
>> (the same for loggers).
>>
>> What probably should happen: in case serverPlatform's class name
>> is the same as the specified, verify that it's classloader is the
>> "main" one (the same one used to load TopLink classes). Otherwise
>> it's a user-defined platform loaded on the temporary classloader
>> - then it should be overridden with the new one (the same class
>> name, but using the passed ("real") classloader).
>>
>> Please let me know does that make sense to you.
>>
>> Thanks,
>>
>> Andrei
>>
>> ----- Original Message -----
>> *From:* Mitesh Meswani <mailto:Mitesh.Meswani_at_Sun.COM>
>> *To:* persistence_at_glassfish.dev.java.net
>> <mailto:persistence_at_glassfish.dev.java.net>
>> *Cc:* Timothy.Quinn_at_Sun.COM <mailto:Timothy.Quinn_at_Sun.COM>
>> *Sent:* Thursday, May 24, 2007 9:16 PM
>> *Subject:* A Question about code in EntityMangerSetupImpl
>>
>> Hi Tom, Gordon,
>>
>> I have a question about following code from EntityMangerSetupImpl
>>
>> protected boolean updateServerPlatform(Map m, ClassLoader
>> loader) {
>> String serverPlatformClassName =
>> PropertiesHandler.getPropertyValueLogDebug(TopLinkProperties.TARGET_SERVER,
>> m, session);
>> ....
>> ....
>> ServerPlatform serverPlatform = null;
>> // New platform - create the new instance and set it.
>> *--->A* *Class cls =
>> findClassForProperty(serverPlatformClassName,
>> TopLinkProperties.TARGET_SERVER, loader); *
>> try {
>> Class clz =
>> oracle.toplink.essentials.internal.sessions.DatabaseSessionImpl.class;
>> *--->B* *Constructor constructor =
>> cls.getConstructor(new
>> Class[]{oracle.toplink.essentials.internal.sessions.DatabaseSessionImpl.class});*
>> serverPlatform =
>> (ServerPlatform)constructor.newInstance(new Object[]{session});
>>
>> Here depending on from which environment and from which
>> method updateServerPlatform is called, the parameter loader
>> is either the classloader that loads the application
>> (PereistenceUnit) or the tempClassLoader which is expected to
>> be a clone.
>>
>> At point (A) above, we use the parameter 'loader' as the
>> classloader to load the class specified by property
>> TopLinkProperties.TARGET_SERVER. At point (B) we try to get
>> constructor for cls with parameter
>> 'oracle.toplink.essentials.internal.sessions.DatabaseSessionImpl.class'.
>> If 'cls' is loaded by a different classloader than the
>> classloader that loads 'DatabaseSessionImpl.class', the
>> constructor will never be found. Infect, this is what is
>> happening inside appclient for glassfish and we are not able
>> to inject persistence artifacts into appclient.
>>
>> The question is at point (A) above, why do we use the
>> parameter 'loader' as the classloader to load the class
>> specified by property TopLinkProperties.TARGET_SERVER?
>>
>> Thanks,
>> Mitesh
>>
>>


/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * // Copyright (c) 1998, 2007, Oracle. All rights reserved.
 *
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License"). You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code. If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 *
 * Contributor(s):
 *
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license." If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above. However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
package oracle.toplink.essentials.internal.ejb.cmp3;

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

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.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.ejb.cmp3.persistence.PersistenceUnitProcessor;

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;

import static oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider.*;

/**
 * INTERNAL:
 * A TopLink specific implementer of the EntityManagerInitializer interface.
 */
public class EntityManagerSetupImpl {
    /*
     * Design Pattern in use: Builder pattern
     * EntityManagerSetupImpl, MetadataProcessor and MetadataProject
     * play the role of director, builder and product respectively.
     * See processORMetadata which is the factory method.
     */

    protected MetadataProcessor processor = null;
    protected PersistenceUnitInfo persistenceUnitInfo = null;
    protected Map predeployProperties = null;
    // count a number of open factories that use this object.
    protected int factoryCount = 0;
    protected ServerSession session = null;
    // true if predeploy called by createContainerEntityManagerFactory; false - createEntityManagerFactory
    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();

    // factoryCount==0; session==null
    public static final String STATE_INITIAL = "Initial";
    
    // session != null
    public static final String STATE_PREDEPLOYED = "Predeployed";
    
    // factoryCount>0; session != null; session stored in SessionManager
    public static final String STATE_DEPLOYED = "Deployed";
           
    // factoryCount==0; session==null
    public static final String STATE_PREDEPLOY_FAILED="PredeployFailed";
    
    // factoryCount>0; session != null
    public static final String STATE_DEPLOY_FAILED = "DeployFailed";
    
    // factoryCount==0; session==null
    public static final String STATE_UNDEPLOYED = "Undeployed";

    protected String state = STATE_INITIAL;

        /**
     * Initial -----> PredeployFailed
     * | |
     * V V
     * |-> Predeployed --> DeployFailed
     * | | | |
     * | V V V
     * | Deployed -> Undeployed-->|
     * | |
     * |<-------------------------V
     */
    
    
    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());
        }
    }
    
    
    /**
     * 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 (in case the session is not connected).
     *
     * Note that there is no need to synchronize deploy method - it doesn't alter factoryCount
     * and while deploy is executed no other method can alter the current state
     * (predeploy call would just increment factoryCount; undeploy call would not drop factoryCount to 0).
     * However precautions should be taken to handle concurrent calls to deploy, because those may
     * alter the current state or connect the session.
     *
     * @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 ServerSession deploy(ClassLoader realClassLoader, Map additionalProperties) {
        if(state != STATE_PREDEPLOYED && state != STATE_DEPLOYED) {
            throw new PersistenceException(EntityManagerSetupException.cannotDeployWithoutPredeploy(persistenceUnitInfo.getPersistenceUnitName(), state));
        }
        // state is PREDEPLOYED or DEPLOYED
        session.log(SessionLog.FINEST, SessionLog.PROPERTIES, "deploy_begin", new Object[]{getPersistenceUnitInfo().getPersistenceUnitName(), state, factoryCount});
        try {
            Map deployProperties = mergeMaps(additionalProperties, predeployProperties);
            translateOldProperties(deployProperties, session);
            
            if(state == STATE_PREDEPLOYED) {
                synchronized(session) {
                    if(state == STATE_PREDEPLOYED) {
                        try {
                            // 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();
                            // free the resouces that we don't need any more.
                            processor.cleanup();
                            processor = null;
                    
                            initServerSession(deployProperties);
                    
                            if (session.getIntegrityChecker().hasErrors()){
                                session.handleException(new IntegrityException(session.getIntegrityChecker()));
                            }
                    
                            session.getDatasourcePlatform().getConversionManager().setLoader(realClassLoader);
                            state = STATE_DEPLOYED;
                        } catch (RuntimeException ex) {
                            state = STATE_DEPLOY_FAILED;
                            // session not discarded here only because it will be used in undeploy method for debug logging.
                            throw new PersistenceException(EntityManagerSetupException.deployFailed(persistenceUnitInfo.getPersistenceUnitName(), ex));
                        }
                    }
                }
            }
            // state is DEPLOYED
            if(!session.isConnected()) {
                synchronized(session) {
                    if(!session.isConnected()) {
                        updateServerSession(deployProperties, realClassLoader);
                        if(isValidationOnly(deployProperties, false)) {
                            session.initializeDescriptors();
                        } else {
                            login(session, deployProperties);
                            generateDDLFiles(session, deployProperties, !isInContainerMode);
                        }
                    }
                }
            }
            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, factoryCount});
        }
    }


    /**
     * 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:
     * Updates the TopLink ServerPlatform class for use with this platform.
     * @returns true if the ServerPlatform has changed.
     */
    protected boolean updateServerPlatform(Map m, ClassLoader loader) {
        String serverPlatformClassName = PropertiesHandler.getPropertyValueLogDebug(TopLinkProperties.TARGET_SERVER, m, session);
        if(serverPlatformClassName == null) {
            // property is not specified - nothing to do.
            return false;
        }

        // originalServerPlatform is always non-null - Session's constructor sets serverPlatform to NoServerPlatform
        ServerPlatform originalServerPlatform = session.getServerPlatform();
        String originalServerPlatformClassName = originalServerPlatform.getClass().getName();
        if(originalServerPlatformClassName.equals(serverPlatformClassName)) {
            // nothing to do - use the same value as before
            return false;
        }

        // the new serverPlatform
        ServerPlatform serverPlatform = null;
        // New platform - create the new instance and set it.
        Class cls = findClassForProperty(serverPlatformClassName, TopLinkProperties.TARGET_SERVER, loader);
        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)) {
                // the new serverPlatform is CustomServerPlatform, cls is its ExternalTransactionController class
                if(originalServerPlatform.getClass().equals(CustomServerPlatform.class)) {
                    // both originalServerPlatform and the new serverPlatform are Custom,
                    // just set externalTransactionController class (if necessary) into
                    // originalServerPlatform
                    CustomServerPlatform originalCustomServerPlatform = (CustomServerPlatform)originalServerPlatform;
                    if(cls.equals(originalCustomServerPlatform.getExternalTransactionControllerClass())) {
                        // externalTransactionController classes are the same - nothing to do
                    } else {
                        originalCustomServerPlatform.setExternalTransactionControllerClass(cls);
                    }
                } else {
                    // originalServerPlatform is not custom - need a new one.
                    CustomServerPlatform customServerPlatform = new CustomServerPlatform(session);
                    customServerPlatform.setExternalTransactionControllerClass(cls);
                    serverPlatform = customServerPlatform;
                }
             } else {
                 throw EntityManagerSetupException.failedToInstantiateServerPlatform(serverPlatformClassName, TopLinkProperties.TARGET_SERVER, ex);
             }
         }
 
        if (serverPlatform != null){
            session.setServerPlatform(serverPlatform);
            return true;
        }
        return false;
    }

    /**
     * INTERNAL:
     * Update loggers and settings for the singleton logger and the session logger.
     * @param m the properties map
     * @param serverPlatformChanged the boolean that denotes a serverPlatform change in the session.
     * @param sessionNameChanged the boolean that denotes a sessionNameChanged change in the session.
     */
    protected void updateLoggers(Map m, boolean serverPlatformChanged, boolean sessionNameChanged, ClassLoader loader) {
        // Logger(SessionLog type) can be specified by the logger property or ServerPlatform.getServerLog().
        // The logger property has a higher priority to ServerPlatform.getServerLog().
        String loggerClassName = PropertiesHandler.getPropertyValueLogDebug(TopLinkProperties.LOGGING_LOGGER, m, session);

        // The sessionLog instance should be different from the singletonLog because they have
        // different state.
        SessionLog singletonLog = null, sessionLog = null;
        if (loggerClassName != null) {
            SessionLog currentLog = session.getSessionLog();
            if (!currentLog.getClass().getName().equals(loggerClassName)) {
                // Logger class was specified and it's not what's already there.
                Class sessionLogClass = findClassForProperty(loggerClassName, TopLinkProperties.LOGGING_LOGGER, loader);
                try {
                    singletonLog = (SessionLog)sessionLogClass.newInstance();
                    sessionLog = (SessionLog)sessionLogClass.newInstance();
                } catch (Exception ex) {
                    throw EntityManagerSetupException.failedToInstantiateLogger(loggerClassName, TopLinkProperties.LOGGING_LOGGER, ex);
                }
            }
        } else if(serverPlatformChanged) {
            ServerPlatform serverPlatform = session.getServerPlatform();
            singletonLog = serverPlatform.getServerLog();
            sessionLog = serverPlatform.getServerLog();
        }
        
        // Don't change default loggers if the new loggers have not been created.
        if (singletonLog != null && sessionLog != null){
            AbstractSessionLog.setLog(singletonLog);
            session.setSessionLog(sessionLog);
        } else if (sessionNameChanged) {
            // In JavaLog this will result in logger name changes,
            // but won't affect DefaultSessionLog.
            // Note, that the session hasn't change, only its name.
            session.getSessionLog().setSession(session);
        }

        //Bug5389828. Update the logging settings for the singleton logger.
        initOrUpdateLogging(m, AbstractSessionLog.getLog());
        initOrUpdateLogging(m, session.getSessionLog());
    }
    
    protected static Class findClass(String className, ClassLoader loader) throws ClassNotFoundException, PrivilegedActionException {
        if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
            return (Class)AccessController.doPrivileged(new PrivilegedClassForName(className, true, loader));
        } else {
            return oracle.toplink.essentials.internal.security.PrivilegedAccessHelper.getClassForName(className, true, loader);
        }
    }
    
    protected static Class findClassForProperty(String className, String propertyName, ClassLoader loader) {
        try {
            return findClass(className, loader);
        } catch (PrivilegedActionException exception1) {
            throw EntityManagerSetupException.classNotFoundForProperty(className, propertyName, exception1.getException());
        } catch (ClassNotFoundException exception2) {
            throw EntityManagerSetupException.classNotFoundForProperty(className, propertyName, exception2);
        }
    }
    
    protected void updateDescriptorCacheSettings(Map m, ClassLoader loader) {
        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, loader);
            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.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, loader);
            }
            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.
     *
     * The first call to this method caches persistenceUnitInfo which is reused in the following calls.
     *
     * Note that in JSE case factoryCount is NOT incremented on the very first call
     * (by JavaSECMPInitializer.callPredeploy, typically in preMain).
     * That provides 1 to 1 correspondence between factoryCount and the number of open factories.
     *
     * In case factoryCount > 0 the method just increments factoryCount.
     * factory == 0 triggers creation of a new session.
     *
     * This method and undeploy - the only methods altering factoryCount - should be synchronized.
     *
     * @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(javax.persistence.spi.PersistenceUnitInfo, java.util.Map)
     */
    public synchronized ClassTransformer predeploy(PersistenceUnitInfo info, Map extendedProperties) {
        if(state == STATE_DEPLOY_FAILED) {
            throw new PersistenceException(EntityManagerSetupException.cannotPredeploy(persistenceUnitInfo.getPersistenceUnitName(), state));
        }
        if(state == STATE_PREDEPLOYED || state == STATE_DEPLOYED) {
            session.log(SessionLog.FINEST, SessionLog.PROPERTIES, "predeploy_begin", new Object[]{getPersistenceUnitInfo().getPersistenceUnitName(), state, factoryCount});
            factoryCount++;
            if(session != null) {
                session.log(SessionLog.FINEST, SessionLog.PROPERTIES, "predeploy_end", new Object[]{getPersistenceUnitInfo().getPersistenceUnitName(), state, factoryCount});
            }
            return null;
        } else if(state == STATE_INITIAL || state == STATE_UNDEPLOYED) {
            persistenceUnitInfo = info;
        }
        // state is INITIAL, PREDEPLOY_FAILED or UNDEPLOYED
        try {
            ClassLoader privateClassLoader = persistenceUnitInfo.getNewTempClassLoader();

            // create server session (it should be done before initializing ServerPlatform)
            // (also before translateOldProperties - it requires session to log warnings)
            session = new ServerSession(new Project(new DatabaseLogin()));
            
            predeployProperties = mergeMaps(extendedProperties, persistenceUnitInfo.getProperties());
    
            // translate old properties
            // this should be done before using properties (i.e. ServerPlatform)
            translateOldProperties(predeployProperties, null);
    
            ClassLoader realClassLoader = persistenceUnitInfo.getClassLoader();
            // ServerSession name and ServerPlatform must be set prior to setting the loggers.
            setServerSessionName(predeployProperties);
            updateServerPlatform(predeployProperties, realClassLoader);
    
            // Update loggers and settings for the singleton logger and the session logger.
            updateLoggers(predeployProperties, true, false, realClassLoader);
            
            warnOldProperties(predeployProperties, session);
    
            session.getPlatform().setConversionManager(new EJB30ConversionManager());
        
            PersistenceUnitTransactionType transactionType=null;
            //find and override the transaction type
            String transTypeString = getConfigPropertyAsStringLogDebug(TopLinkProperties.TRANSACTION_TYPE, predeployProperties, session);
            if ( transTypeString != null ){
                transactionType=PersistenceUnitTransactionType.valueOf(transTypeString);
            }else if (persistenceUnitInfo!=null){
                transactionType=persistenceUnitInfo.getTransactionType();
            }
            if(!isValidationOnly(predeployProperties, false) && persistenceUnitInfo != null && transactionType == PersistenceUnitTransactionType.JTA) {
                if( predeployProperties.get(TopLinkProperties.JTA_DATASOURCE) == null && 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 || state == STATE_UNDEPLOYED) {
                enableLazyForOneToOne = true;
                String weaving = getConfigPropertyAsString(TopLinkProperties.WEAVING);
                if (weaving != null && weaving.equalsIgnoreCase("false")) {
                    enableLazyForOneToOne = false;
                }
            }
            
            boolean throwExceptionOnFail = "true".equalsIgnoreCase(
                    EntityManagerFactoryProvider.getConfigPropertyAsStringLogDebug(EntityManagerFactoryProvider.TOPLINK_ORM_THROW_EXCEPTIONS, predeployProperties, "true", session));
    
            // Create an instance of MetadataProcessor for specified persistence unit info
            processor = new MetadataProcessor(persistenceUnitInfo, session, privateClassLoader, enableLazyForOneToOne);
            
            // Process the Object/relational metadata from XML and annotations.
            PersistenceUnitProcessor.processORMetadata(processor,privateClassLoader, session,throwExceptionOnFail);
    
            
            // 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){
                // build a list of entities the persistence unit represented by this EntityManagerSetupImpl will use
                Collection entities = PersistenceUnitProcessor.buildEntityList(processor,privateClassLoader);
                transformer = TransformerFactory.createTransformerAndModifyProject(session, entities, privateClassLoader);
            }
            
            // factoryCount is not incremented only in case of a first call to preDeploy
            // in non-container mode: this call is not associated with a factory
            // but rather done by JavaSECMPInitializer.callPredeploy (typically in preMain).
            if((state != STATE_INITIAL && state != STATE_UNDEPLOYED) || this.isInContainerMode()) {
                factoryCount++;
            }
            state = STATE_PREDEPLOYED;
            session.log(SessionLog.FINEST, SessionLog.PROPERTIES, "predeploy_end", new Object[]{getPersistenceUnitInfo().getPersistenceUnitName(), state, factoryCount});
            return transformer;
        } catch (RuntimeException ex) {
            state = STATE_PREDEPLOY_FAILED;
            session = null;
            throw new PersistenceException(EntityManagerSetupException.predeployFailed(persistenceUnitInfo.getPersistenceUnitName(), ex));
        }
    }

    /**
     * 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 defaultValue
     * @return
     */
    public String getConfigPropertyAsString(String propertyKey, String defaultValue){
        return getConfigPropertyAsStringLogDebug(propertyKey, predeployProperties, defaultValue, session);
    }

    public String getConfigPropertyAsString(String propertyKey){
        return 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 = getConfigPropertyAsStringLogDebug(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 mergeMaps(m, predeployProperties);
        } else if(persistenceUnitInfo != null) {
            return mergeMaps(m, persistenceUnitInfo.getProperties());
        } else {
            return m;
        }
    }

    public boolean isInContainerMode(){
        return isInContainerMode;
    }

    /**
   * 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 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 = getConfigPropertyAsStringLogDebug(TopLinkProperties.JDBC_USER, m, session);
        String password = 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);
        }
        PersistenceUnitTransactionType transactionType = persistenceUnitInfo.getTransactionType();
        //find and override the transaction type
        String transTypeString = getConfigPropertyAsStringLogDebug(TopLinkProperties.TRANSACTION_TYPE, m, session);
        if ( transTypeString != null ){
            transactionType = PersistenceUnitTransactionType.valueOf(transTypeString);
        }
        //find the jta datasource
        javax.sql.DataSource jtaDatasource = getDatasourceFromProperties(m, TopLinkProperties.JTA_DATASOURCE, persistenceUnitInfo.getJtaDataSource());

        //find the non jta datasource
        javax.sql.DataSource nonjtaDatasource = getDatasourceFromProperties(m, TopLinkProperties.NON_JTA_DATASOURCE, persistenceUnitInfo.getNonJtaDataSource());

        if (isValidationOnly(m, false) && transactionType == PersistenceUnitTransactionType.JTA && jtaDatasource == null){
            updateLoginDefaultConnector(login, m);
            return;
        }
        
        login.setUsesExternalTransactionController(transactionType == 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 = jtaDatasource;
            // only define readDatasource if there is jta mainDatasource
            readDatasource = nonjtaDatasource;
        } else {
            // JtaDataSource will be ignored because transactionType is RESOURCE_LOCAL
            if(jtaDatasource != null) {
                session.log(SessionLog.WARNING, SessionLog.TRANSACTION, "resource_local_persistence_init_info_ignores_jta_data_source", persistenceUnitInfo.getPersistenceUnitName());
            }
            if(nonjtaDatasource != null) {
                mainDatasource = nonjtaDatasource;
            } 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);
        }
        
    }
    
  /**
   * This is used to return either the defaultDatasource or, if one exists, a datasource
   * defined under the property from the Map m. This method will build a DataSourceImpl
   * object to hold the url if the property in Map m defines a string instead of a datasource.
   */
    protected javax.sql.DataSource getDatasourceFromProperties(Map m, String property, javax.sql.DataSource defaultDataSource){
        Object datasource = getConfigPropertyLogDebug(property, m, session);
        if ( datasource == null ){
            return defaultDataSource;
        }
        if ( datasource instanceof String){
            // Create a dummy DataSource that will throw an exception on access
            return new DataSourceImpl((String)datasource, null, null, null);
        }
        if ( !(datasource instanceof javax.sql.DataSource) ){
            //A warning should be enough. Though an error might be better, the properties passed in could contain anything
            session.log(SessionLog.WARNING, SessionLog.PROPERTIES, "invalid_datasource_property_value", property, datasource);
            return defaultDataSource;
        }
        return (javax.sql.DataSource)datasource;
    }
    
  /**
   * 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 = getConfigPropertyAsStringLogDebug(TopLinkProperties.JDBC_DRIVER, m, session);
            String connectionString = 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 = getConfigPropertyAsStringLogDebug(TopLinkProperties.JDBC_WRITE_CONNECTIONS_MIN, m, session);
            if(strWriteMin != null) {
                session.getDefaultConnectionPool().setMinNumberOfConnections(Integer.parseInt(strWriteMin));
            }
            String strWriteMax = 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 = getConfigPropertyAsStringLogDebug(TopLinkProperties.JDBC_READ_CONNECTIONS_MIN, m, session);
            if(strReadMin != null) {
                session.getReadConnectionPool().setMinNumberOfConnections(Integer.parseInt(strReadMin));
            }
            String strReadMax = getConfigPropertyAsStringLogDebug(TopLinkProperties.JDBC_READ_CONNECTIONS_MAX, m, session);
            if(strReadMax != null) {
                session.getReadConnectionPool().setMaxNumberOfConnections(Integer.parseInt(strReadMax));
            }
            String strShouldUseShared = 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 and registers the session.
   * 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 m
   */
    protected void initServerSession(Map m) {
        assignCMP3Policy();

        // Register session that has been created earlier.
        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);
            }
        }
    }

  /**
   * Set ServerSession name but do not register the session.
   * The session registration should be done in sync
   * with increment of the deployment counter, as otherwise the
   * undeploy will not behave correctly in case of a more
   * than one predeploy request for the same session name.
   * @param m the combined properties map.
   */
    protected void setServerSessionName(Map m) {
        // 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);
    }

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

        // In deploy Session name and ServerPlatform could've changed which will affect the loggers.
        boolean serverPlatformChanged = updateServerPlatform(m, loader);
        boolean sessionNameChanged = updateSessionName(m);

        updateLoggers(m, serverPlatformChanged, sessionNameChanged, loader);

        String shouldBindString = 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();
        }
        

        updatePools(m);
        
        updateDescriptorCacheSettings(m, loader);

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

  /**
   * 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, ClassLoader loader) {
        String sessionCustomizerClassName = getConfigPropertyAsStringLogDebug(TopLinkProperties.SESSION_CUSTOMIZER, m, session);
        if(sessionCustomizerClassName == null) {
            return;
        }
        
        Class sessionCustomizerClass = findClassForProperty(sessionCustomizerClassName, TopLinkProperties.SESSION_CUSTOMIZER, loader);
        SessionCustomizer sessionCustomizer;
        try {
            sessionCustomizer = (SessionCustomizer)sessionCustomizerClass.newInstance();
            sessionCustomizer.customize(session);
        } catch (Exception ex) {
            throw EntityManagerSetupException.failedWhileProcessingProperty(TopLinkProperties.SESSION_CUSTOMIZER, sessionCustomizerClassName, ex);
        }
   }

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

    /**
     * Updates server session name if changed.
     * @return true if the name has changed.
     */
    protected boolean updateSessionName(Map m) {
        String newName = getConfigPropertyAsStringLogDebug(TopLinkProperties.SESSION_NAME, m, session);
        if(newName == null || newName.equals(session.getName())) {
            return false;
        }

        removeSessionFromGlobalSessionManager();
        session.setName(newName);
        addSessionToGlobalSessionManager();

        return true;
    }
    
    protected void processDescriptorCustomizers(Map m, ClassLoader loader) {
        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, loader);
                    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, loader);
                try {
                    DescriptorCustomizer customizer = (DescriptorCustomizer)customizerClass.newInstance();
                    customizer.customize(descriptor);
                } catch (Exception ex) {
                    throw EntityManagerSetupException.failedWhileProcessingProperty(TopLinkProperties.DESCRIPTOR_CUSTOMIZER_ + name, customizerClassName, ex);
                }
            }
        }
    }
    
    
    public boolean isInitial() {
        return state == STATE_INITIAL;
    }

    public boolean isPredeployed() {
        return state == STATE_PREDEPLOYED;
    }

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

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

    public boolean isPredeployFailed() {
        return state == STATE_PREDEPLOY_FAILED;
    }

    public boolean isDeployFailed() {
        return state == STATE_DEPLOY_FAILED;
    }

    public int getFactoryCount() {
        return factoryCount;
    }

    public boolean shouldRedeploy() {
        return state == STATE_UNDEPLOYED || state == STATE_PREDEPLOY_FAILED;
    }
    
    /**
     * Undeploy may be called several times, but only the call that decreases
     * factoryCount to 0 disconnects the session and removes it from the session manager.
     * This method and predeploy - the only methods altering factoryCount - should be synchronized.
     * After undeploy call that turns factoryCount to 0:
     * session==null;
     * PREDEPLOYED, DEPLOYED and DEPLOYED_FAILED states change to UNDEPLOYED state.
     */
    public synchronized void undeploy() {
        if(state == STATE_INITIAL || state == STATE_PREDEPLOY_FAILED || state == STATE_UNDEPLOYED) {
            // must already have factoryCount==0 and session==null
            return;
        }
        // state is PREDEPLOYED, DEPLOYED or DEPLOY_FAILED
        session.log(SessionLog.FINEST, SessionLog.PROPERTIES, "undeploy_begin", new Object[]{getPersistenceUnitInfo().getPersistenceUnitName(), state, factoryCount});
        try {
            factoryCount--;
            if(factoryCount > 0) {
                return;
            }
            state = STATE_UNDEPLOYED;
            removeSessionFromGlobalSessionManager();
        } finally {
            session.log(SessionLog.FINEST, SessionLog.PROPERTIES, "undeploy_end", new Object[]{getPersistenceUnitInfo().getPersistenceUnitName(), state, factoryCount});
            if(state == STATE_UNDEPLOYED) {
                session = null;
            }
        }
    }
}


/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * // Copyright (c) 1998, 2007, Oracle. All rights reserved.
 *
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License"). You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code. If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 *
 * Contributor(s):
 *
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license." If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above. However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
package oracle.toplink.essentials.internal.ejb.cmp3;

import java.util.*;
import java.net.URL;
import java.net.URLClassLoader;
import java.lang.instrument.*;
import java.security.ProtectionDomain;

import oracle.toplink.essentials.ejb.cmp3.persistence.SEPersistenceUnitInfo;

import oracle.toplink.essentials.logging.AbstractSessionLog;
import oracle.toplink.essentials.internal.ejb.cmp3.EntityManagerSetupImpl;
import oracle.toplink.essentials.ejb.cmp3.persistence.PersistenceUnitProcessor;
import oracle.toplink.essentials.ejb.cmp3.persistence.Archive;
import oracle.toplink.essentials.exceptions.*;
import oracle.toplink.essentials.logging.SessionLog;
import oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider;
import oracle.toplink.essentials.PersistenceProvider;

import javax.persistence.PersistenceException;
import javax.persistence.spi.ClassTransformer;
import oracle.toplink.essentials.config.TopLinkProperties;

/**
 * INTERNAL:
 *
 * JavaSECMPInitializer is used to bootstrap the deployment of EntityBeans in EJB 3.0
 * when deployed in a non-managed setting
 *
 * It is called internally by our Provider
 *
 * @see oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider
 */
public class JavaSECMPInitializer {

    // Used when byte code enhancing
    public static Instrumentation globalInstrumentation;

    // The internal loader is used by applications that do weaving to pre load classes
    // When this flag is set to false, we will not be able to weave.
    protected boolean shouldCreateInternalLoader = true;

    // The JavaSECMPInitializer - a singleton
    protected static JavaSECMPInitializer javaSECMPInitializer;

    protected ClassLoader sessionClassLoader = null;

    /**
     * INTERNAL:
     * Get the singleton entityContainer.
     * @return EnityContainer
     */
    public static synchronized JavaSECMPInitializer getJavaSECMPInitializer() {
        if (javaSECMPInitializer == null) {
           javaSECMPInitializer = new JavaSECMPInitializer();
        }
        AbstractSessionLog.getLog().setLevel(JavaSECMPInitializer.getTopLinkLoggingLevel());
        return javaSECMPInitializer;
    }
    
  /**
   * Return whether initialization has occured without actually triggering
   * initialization
   */
    public static boolean isSingletonInitialized(){
        return javaSECMPInitializer != null;
    }

  /**
   * Look in the System properties for a logging level property and return a integer
   * that can be used to set the logging level in TopLink
   * @return
   */
    public static int getTopLinkLoggingLevel(){
        String logLevel = System.getProperty(TopLinkProperties.LOGGING_LEVEL);
        return AbstractSessionLog.translateStringToLoggingLevel(logLevel);
    }

    /**
     * INTERNAL:
     * User should not instantiate JavaSECMPInitializer.
     */
    protected JavaSECMPInitializer() {
        super();
    }

    /**
     * INTERNAL
     * predeploy (with deploy) is one of the two steps required in deployment of entities
     * This method will prepare to call predeploy, call it and finally register the
     * transformer returned to be used for weaving.
     */
    protected boolean callPredeploy(SEPersistenceUnitInfo persistenceUnitInfo, Map m) {
        ClassLoader tempLoader = null;
        // we will only attempt to deploy when TopLink is specified as the provider or the provider is unspecified
        String providerClassName = persistenceUnitInfo.getPersistenceProviderClassName();
        if (providerClassName == null || providerClassName.equals("") || providerClassName.equals(EntityManagerFactoryProvider.class.getName()) || providerClassName.equals(PersistenceProvider.class.getName())){
            EntityManagerSetupImpl emSetupImpl = EntityManagerFactoryProvider.getEntityManagerSetupImpl(persistenceUnitInfo.getPersistenceUnitName());
            // if we already have an EntityManagerSetupImpl this PU has already been processed. Use the existing one
            if (emSetupImpl != null && !emSetupImpl.isUndeployed()){
                return false;
            }
            Set tempLoaderSet = PersistenceUnitProcessor.buildClassSet(persistenceUnitInfo, Thread.currentThread().getContextClassLoader());

            Map mergedProperties = EntityManagerFactoryProvider.mergeMaps(m, persistenceUnitInfo.getProperties());

            String weaving = EntityManagerFactoryProvider.getConfigPropertyAsString(TopLinkProperties.WEAVING, mergedProperties, null);
            // Bug#4452468 When globalInstrumentation is null, there is no weaving
            if (globalInstrumentation == null) {
                if (weaving == null) {
                   mergedProperties.put(TopLinkProperties.WEAVING, "false");
                }else if (weaving.equalsIgnoreCase("true")){
                    throw new PersistenceException(EntityManagerSetupException.wrongWeavingPropertyValue());
                }
            }
            
            // Bug#2741: If weaving disabled then use regular loader, not a temp one
            if (weaving != null && weaving.equalsIgnoreCase("false")) {
                shouldCreateInternalLoader=false;
            }
            // Create the temp loader that will not cache classes for entities in our persistence unit
            tempLoader = createTempLoader(tempLoaderSet);
            persistenceUnitInfo.setNewTempClassLoader(tempLoader);
            persistenceUnitInfo.setClassLoader(getMainLoader());
            if (emSetupImpl == null){
                emSetupImpl = new EntityManagerSetupImpl();
                EntityManagerFactoryProvider.addEntityManagerSetupImpl(persistenceUnitInfo.getPersistenceUnitName(), emSetupImpl);
            }
            // Make the callback
            AbstractSessionLog.getLog().log(SessionLog.FINER, "cmp_init_invoke_predeploy", persistenceUnitInfo.getPersistenceUnitName());

            // A call to predeploy will partially build the session we will use
            final ClassTransformer transformer = emSetupImpl.predeploy(persistenceUnitInfo, mergedProperties);
    
            // If we got a transformer then register it
            if ((transformer != null) && (globalInstrumentation != null)) {
                AbstractSessionLog.getLog().log(SessionLog.FINER, "cmp_init_register_transformer", persistenceUnitInfo.getPersistenceUnitName());
                globalInstrumentation.addTransformer(new ClassFileTransformer(){
                    // adapt ClassTransformer to ClassFileTransformer interface
                    public byte[] transform(
                            ClassLoader loader, String className,
                            Class<?> classBeingRedefined,
                            ProtectionDomain protectionDomain,
                            byte[] classfileBuffer) throws IllegalClassFormatException {
                        return transformer.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer);
                    }
                });
            } else if (transformer == null) {
                AbstractSessionLog.getLog().log(SessionLog.FINER, "cmp_init_transformer_is_null");
            } else if (globalInstrumentation == null) {
                AbstractSessionLog.getLog().log(SessionLog.FINER, "cmp_init_globalInstrumentation_is_null");
            }
            return true;
        }
        return false;
    }
    
    /**
 * Create a temporary class loader that can be used to inspect classes and then
 * thrown away. This allows classes to be introspected prior to loading them
 * with application's main class loader enabling weaving.
 */
    protected ClassLoader createTempLoader(Collection col) {
        return createTempLoader(col, true);
    }

    protected ClassLoader createTempLoader(Collection col, boolean shouldOverrideLoadClassForCollectionMembers) {
        if (!shouldCreateInternalLoader) {
            return Thread.currentThread().getContextClassLoader();
        }

        ClassLoader currentLoader = Thread.currentThread().getContextClassLoader();
        if (!(currentLoader instanceof URLClassLoader)) {
            //we can't create a TempEntityLoader so just use the current one
            //shouldn't be a problem (and should only occur) in JavaSE
            return currentLoader;
        }
        URL[] urlPath = ((URLClassLoader)currentLoader).getURLs();
        ClassLoader tempLoader = new TempEntityLoader(urlPath, currentLoader, col, shouldOverrideLoadClassForCollectionMembers);

        AbstractSessionLog.getLog().log(SessionLog.FINER, "cmp_init_tempLoader_created", tempLoader);
        AbstractSessionLog.getLog().log(SessionLog.FINER, "cmp_init_shouldOverrideLoadClassForCollectionMembers", new Boolean(shouldOverrideLoadClassForCollectionMembers));

        return tempLoader;
    }

    public static ClassLoader getMainLoader() {
        return Thread.currentThread().getContextClassLoader();
    }

  /**
   * Initialize one persistence unit.
   * Initialization is a two phase process. First the predeploy process builds the metadata
   * and creates any required transformers.
   * Second the deploy process creates a TopLink session based on that metadata.
   */
    protected void initPersistenceUnits(Archive archive, Map m) {
        Iterator<SEPersistenceUnitInfo> persistenceUnits = PersistenceUnitProcessor.getPersistenceUnits(archive, sessionClassLoader).iterator();
        while (persistenceUnits.hasNext()){
            SEPersistenceUnitInfo persistenceUnitInfo = persistenceUnits.next();
            callPredeploy(persistenceUnitInfo, m);
        }
    }

    /**
     * INTERNAL
     * This method initializes the container. Essentially, it will try to load the
     * class that contains the list of entities and reflectively call the method that
     * contains that list. It will then initialize the container with that list.
     * If succeeded return true, false otherwise.
     */
    public void initialize(Map m) {
        sessionClassLoader = getMainLoader();
        final Set<Archive> pars = PersistenceUnitProcessor.findPersistenceArchives();
        for (Archive archive: pars){
            AbstractSessionLog.getLog().log(SessionLog.FINER, "cmp_init_initialize", archive);
            initPersistenceUnits(archive, m);
        }
    }

    /**
     * INTERNAL:
     * Should be called only by the agent. (when weaving classes)
     * If succeeded return true, false otherwise.
     */
    protected static void initializeFromAgent(Instrumentation instrumentation) throws Exception {
        AbstractSessionLog.getLog().setLevel(JavaSECMPInitializer.getTopLinkLoggingLevel());

        // Squirrel away the instrumentation for later
        globalInstrumentation = instrumentation;
        // Create JavaSECMPInitializer singleton
        javaSECMPInitializer = new JavaSECMPInitializer();
        // Initialize it
        javaSECMPInitializer.initialize(new HashMap());
    }

    /**
     * INTERNAL:
     * Create a list of java.lang.Class that contains the classes of all the entities
     * that we will be deploying
     */
    protected Set loadEntityClasses(Collection entityNames, ClassLoader classLoader) {
        Set entityClasses = new HashSet();

        // Load the classes using the loader passed in
        AbstractSessionLog.getLog().log(SessionLog.FINER, "cmp_loading_entities_using_loader", classLoader);
        for (Iterator iter = entityNames.iterator(); iter.hasNext();) {
            String entityClassName = (String)iter.next();
            try {
                entityClasses.add(classLoader.loadClass(entityClassName));
            } catch (ClassNotFoundException cnfEx) {
                throw ValidationException.entityClassNotFound(entityClassName, classLoader, cnfEx);
            }
        }
        return entityClasses;
    }

    /*********************************/
    /***** Temporary Classloader *****/
    /*********************************/
    /** This class loader is provided at initialization time to allow us to temporarily load
     * domain classes so we can examine them for annotations. After they are loaded we will throw this
     * class loader away. Transformers can then be registered on the real class loader to allow
     * weaving to occur.
     *
     * It selectively loads classes based on the list of classnames it is instantiated with. Classes
     * not on that list are allowed to be loaded by the parent.
     */
    public class TempEntityLoader extends URLClassLoader {
        Collection classNames;
        boolean shouldOverrideLoadClassForCollectionMembers;
        
        //added to resolved gf #589 - without this, the orm.xml url would be returned twice
        public Enumeration<URL> getResources(String name) throws java.io.IOException {
            return this.getParent().getResources(name);
        }

        public TempEntityLoader(URL[] urls, ClassLoader parent, Collection classNames, boolean shouldOverrideLoadClassForCollectionMembers) {
            super(urls, parent);
            this.classNames = classNames;
            this.shouldOverrideLoadClassForCollectionMembers = shouldOverrideLoadClassForCollectionMembers;
        }

        public TempEntityLoader(URL[] urls, ClassLoader parent, Collection classNames) {
            this(urls, parent, classNames, true);
        }

        // Indicates if the classLoad should be overridden for the passed className.
        // Returns true in case the class should NOT be loaded by parent classLoader.
        protected boolean shouldOverrideLoadClass(String name) {
            if (shouldOverrideLoadClassForCollectionMembers) {
                // Override classLoad if the name is in collection
                return (classNames != null) && classNames.contains(name);
            } else {
                // Directly opposite: Override classLoad if the name is NOT in collection.
                // Forced to check for java. and javax. packages here, because even if the class
                // has been loaded by parent loader we would load it again
                // (see comment in loadClass)
                return !name.startsWith("java.") && !name.startsWith("javax.") && ((classNames == null) || !classNames.contains(name));
            }
        }

        protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
            if (shouldOverrideLoadClass(name)) {
                // First, check if the class has already been loaded.
                // Note that the check only for classes loaded by this loader,
                // it doesn't return true if the class has been loaded by parent loader
                // (forced to live with that because findLoadedClass method defined as final protected:
                // neither can override it nor call it on the parent loader)
                Class c = findLoadedClass(name);
                if (c == null) {
                    c = findClass(name);
                }
                if (resolve) {
                    resolveClass(c);
                }
                return c;
            } else {
                return super.loadClass(name, resolve);
            }
        }
    }

}