Andrei, Tom, Wonseok,
Attached is the updated version. I tested the logging part only
in the container so far (unit tests passed, but I didn't test
specifically the logging behavior - what can I be looking for?).
Wonseok, I didn't change code in AbstractSessionLog.setSession()
as we should not be changing the session at any time, but added
a comment when we call the setSession() method.
I can also change
AbstractSessionLog.setLog(new DefaultSessionLog());
session.setSessionLog(new DefaultSessionLog());
to
AbstractSessionLog.setLog(new DefaultSessionLog());
session.setSessionLog(AbstractSessionLog.getLog());
to use cloning inside session's method (I don't see anything in
the DefaultSessionLog, that can cause a problem in this case).
On the other hand, the current code doesn't need extra explanation ;).
thanks,
-marina
Andrei Ilitchev wrote On 11/01/06 07:12,:
> Marina,
>
> Session's constructor sets serverPlatform to NoServerPlatform - don't check
> for null, check for NoServerPlatform.
> ----- Original Message -----
> From: "Marina Vatkina" <Marina.Vatkina_at_Sun.COM>
> To: <persistence_at_glassfish.dev.java.net>
> Sent: Tuesday, October 31, 2006 6:15 PM
> Subject: Re: adding server platform and session name change into logger
> integration
>
>
>
>>Which makes it even more complicated :(.
>>
>>The current code in the logging (no, you can't ignore it when you touch
>>the server platform or the session name) relies at 'null' as the "default"
>>logger use case. Will it be safe to check 'instanceof NoServerPlatform' in
>>addition to null?
>>
>>thanks,
>>-marina
>>
>>Andrei Ilitchev wrote:
>>
>>>Marina,
>>>
>>>PropertyHandler returns default value (which is NoServerPlatform) in case
>>>an property value is an empty string.
>>>So to get back NoServerPlatform, the user should set the property to
>>>either an empty string, or "None", or
>>>"oracle.toplink.essentials.platform.server.NoServerPlatform.
>>>
>>>
>>>----- Original Message ----- From: "Marina Vatkina"
>>><Marina.Vatkina_at_Sun.COM>
>>>To: <persistence_at_glassfish.dev.java.net>
>>>Sent: Tuesday, October 31, 2006 4:00 PM
>>>Subject: Re: adding server platform and session name change into logger
>>>integration
>>>
>>>
>>>
>>>>Hi Andrei,
>>>>
>>>>Yes, this is better. But I'm confused... You filed an issue 1333 that
>>>>server
>>>>platform can be unset in the map. How will it be specified by the user?
>>>>If
>>>>a) as an empty string - your code doesn't handle this case.
>>>>b) as "NoServerPlatform" - will you use reflection to create it?
>>>>
>>>>thanks,
>>>>-marina
>>>>
>>>>Andrei Ilitchev wrote:
>>>>
>>>>
>>>>>Let's try another file type...
>>>>>----- Original Message ----- From: "Marina Vatkina"
>>>>><Marina.Vatkina_at_Sun.COM>
>>>>>To: <persistence_at_glassfish.dev.java.net>
>>>>>Sent: Tuesday, October 31, 2006 2:28 PM
>>>>>Subject: Re: adding server platform and session name change into logger
>>>>>integration
>>>>>
>>>>>
>>>>>
>>>>>>Andrei,
>>>>>>
>>>>>>Can you please resend the file? It came as 0 size.
>>>>>>
>>>>>>thanks,
>>>>>>-marina
>>>>>>
>>>>>>Andrei Ilitchev wrote:
>>>>>>
>>>>>>
>>>>>>>Hi Marina, Wonseok,
>>>>>>> Attached is my version of changes to EntityManagerSetupImpl class.
>>>>>>> I concentrated on handling of serverPlatform property and didn't
>>>>>>>look at the logging stuff.
>>>>>>> Thanks,
>>>>>>> Andrei
>>>>>>>
>>>>>>> ----- Original Message -----
>>>>>>> *From:* Wonseok Kim <mailto:guruwons_at_gmail.com>
>>>>>>> *To:* persistence_at_glassfish.dev.java.net
>>>>>>> <mailto:persistence_at_glassfish.dev.java.net>
>>>>>>> *Sent:* Tuesday, October 31, 2006 11:20 AM
>>>>>>> *Subject:* Re: adding server platform and session name change
>>>>>>>into
>>>>>>> logger integration
>>>>>>>
>>>>>>> Hi Marina,
>>>>>>> Overall, it looks good to me.
>>>>>>>
>>>>>>> Some minor comments below.
>>>>>>>
>>>>>>> protected void updateServerSession(Map m) {
>>>>>>> if (session == null || session.isConnected()) {
>>>>>>> return;
>>>>>>> }
>>>>>>>
>>>>>>> // Session name and ServerPlatform could've changed which
>>>>>>> will affect the loggers.
>>>>>>> boolean serverPlatformChanged =
>>>>>>>setOrUpdateServerPlatform(m);
>>>>>>> boolean sessionNameChanged = updateSessionName(m);
>>>>>>>
>>>>>>> if (serverPlatformChanged) {
>>>>>>> // Update loggers and settings for the singleton
>>>>>>>logger
>>>>>>> and the session logger.
>>>>>>> updateLoggers(predeployProperties);
>>>>>>> } else if (sessionNameChanged) {
>>>>>>> // In JavaLog this will result in logger name changes,
>>>>>>> // but won't affect DefaultSessionLog.
>>>>>>> session.getSessionLog().setSession(session);
>>>>>>> }
>>>>>>>
>>>>>>> -> you mean updateLoggers(m), right?
>>>>>>>
>>>>>>> AbstractSessionLog.setSession() need to be changed to accept new
>>>>>>> session, see below.
>>>>>>>
>>>>>>> public void setSession(Session session) {
>>>>>>> if (this.session == null) {
>>>>>>> this.session = session;
>>>>>>> buildSessionType();
>>>>>>> buildSessionHashCode();
>>>>>>> }
>>>>>>> }
>>>>>>>
>>>>>>> initOrUpdateLogging() doesn't need 'init' parameter anymore after
>>>>>>> your fix for issue 1107.
>>>>>>>
>>>>>>> protected void initOrUpdateLogging(boolean init, Map m,
>>>>>>> SessionLog log) {
>>>>>>>
>>>>>>> Cheers,
>>>>>>> -Wonseok
>>>>>>>
>>>>>>>
>>>>>>> On 10/31/06, *Marina Vatkina* <Marina.Vatkina_at_sun.com
>>>>>>> <mailto:Marina.Vatkina_at_sun.com>> wrote:
>>>>>>>
>>>>>>> Tom, Andrei, Wonseok,
>>>>>>>
>>>>>>> Attached is the changed file with the ideas for fixing this
>>>>>>> problem. I
>>>>>>> didn't test it yet, but would like to start the discussion as
>>>>>>> the practice
>>>>>>> shows that changing EMSetupImpl is a painful exercise.
>>>>>>>
>>>>>>> Probably the main change for you guys is replacing void with
>>>>>>>a
>>>>>>> boolean
>>>>>>> as the returning type for updateSessionName(). Please do not
>>>>>>> panic, but
>>>>>>> suggest your ideas if you don't like it - I need to know if
>>>>>>>the
>>>>>>> name has
>>>>>>> changed and would like to avoid saving the name, then
>>>>>>>comparing
>>>>>>> it again
>>>>>>> after it had been replaced or not: the comparison is done
>>>>>>>inside
>>>>>>> the method
>>>>>>> anyway. Keep in mind that if the server platform has changed,
>>>>>>>I
>>>>>>> need the
>>>>>>> new session name before fixing the loggers.
>>>>>>>
>>>>>>> As you can see, in case of the server platform change, all of
>>>>>>> the loggers
>>>>>>> need to be replaced (we can probably go further and check the
>>>>>>> actual logger
>>>>>>> type change, but I don't think it's worth the effort), in
>>>>>>> particular when
>>>>>>> the value is unset in the Map.
>>>>>>>
>>>>>>> If only the session name change, it seems to be enough to
>>>>>>>update
>>>>>>> the session
>>>>>>> in the existing logger (and do not touch the default logger
>>>>>>>at all).
>>>>>>>
>>>>>>> Wonseok, that also meant moving initOrUpdateLogging() outside
>>>>>>>the
>>>>>>> updateLoggers() call. I also removed parameters from the
>>>>>>>method
>>>>>>> signature
>>>>>>> where they are available as instance variable.
>>>>>>>
>>>>>>> If you all agree, I'll start running the tests. If not, I'd
>>>>>>>like
>>>>>>> to sort
>>>>>>> everything out before going further.
>>>>>>>
>>>>>>> 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 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.DefaultSessionLog;
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.platform.server.NoServerPlatform;
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;
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;
// may be positive only in STATE_DEPLOYED
protected int deploymentCount = 0;
protected ServerSession session = null;
protected ServerPlatform serverPlatform = 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());
}
}
/**
* 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 loader
* @return
*/
private Collection<Class> buildEntityList(ClassLoader loader) {
ArrayList<Class> entityList = new ArrayList<Class>();
for (String className : processor.getProject().getEntityNames()) {
try {
Class entityClass = loader.loadClass(className);
entityList.add(entityClass);
} catch (ClassNotFoundException exc) {
AbstractSessionLog.getLog().log(SessionLog.WARNING,
"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 = mergeMaps(additionalProperties, predeployProperties);
translateOldProperties(deployProperties, session);
if(state == STATE_PREDEPLOYED) {
// 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);
}
deploymentCount++;
state = STATE_DEPLOYED;
try{
updateServerSession(deployProperties);
if(!session.isConnected()) {
if(isValidationOnly(deployProperties, false)) {
session.initializeDescriptors();
} else {
login(session, deployProperties);
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:
* Updates the TopLink ServerPlatform class for use with this platform.
* @returns true if the ServerPlatform has changed.
*/
protected boolean updateServerPlatform(Map m) {
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);
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.
*/
protected void updateLoggers(Map m){
SessionLog currentLog = AbstractSessionLog.getLog();
// 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);
if (currentLog.getClass().getName().equals(loggerClassName)) {
// Nothing to do, it's already set.
return;
}
Class sessionLogClass = null;
if (loggerClassName != null) {
sessionLogClass = findClassForProperty(loggerClassName, TopLinkProperties.LOGGING_LOGGER);
}
// Set loggers for the singleton logger and the session logger.
// sessionLog instance should be different from singletonLog because two log have different state.
SessionLog singletonLog = null, sessionLog = null;
ServerPlatform serverPlatform = session.getServerPlatform();
if(sessionLogClass != null){
try {
singletonLog = (SessionLog)sessionLogClass.newInstance();
sessionLog = (SessionLog)sessionLogClass.newInstance();
} catch (Exception ex) {
throw EntityManagerSetupException.failedToInstantiateLogger(loggerClassName, TopLinkProperties.LOGGING_LOGGER, ex);
}
} else if(!(serverPlatform instanceof NoServerPlatform)){
// We won't set loggers for the default ServerPlatform.
singletonLog = serverPlatform.getServerLog();
sessionLog = serverPlatform.getServerLog();
}
// Don't change default loggers if null, but reset them if the serverPlatform was reset
if (singletonLog != null && sessionLog != null){
AbstractSessionLog.setLog(singletonLog);
session.setSessionLog(sessionLog);
} else if (!(currentLog instanceof DefaultSessionLog)) {
// This means the ServerPlatform has been reset to the default in deploy. Reset the loggers.
AbstractSessionLog.setLog(new DefaultSessionLog());
session.setSessionLog(new DefaultSessionLog());
}
}
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.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(javax.persistence.spi.PersistenceUnitInfo, java.util.Map)
*/
public ClassTransformer predeploy(PersistenceUnitInfo info, Map extendedProperties) {
if(state == STATE_INITIAL) {
persistenceUnitInfo = info;
}
ClassLoader privateClassLoader = persistenceUnitInfo.getNewTempClassLoader();
predeployProperties = mergeMaps(extendedProperties, persistenceUnitInfo.getProperties());
// translate old properties
// this should be done before using properties (i.e. ServerPlatform)
translateOldProperties(predeployProperties, null);
// create server session (it should be done before initializing ServerPlatform)
session = new ServerSession(new Project(new DatabaseLogin()));
// ServerSession name must be set prior to setting the loggers.
setServerSessionName(predeployProperties);
// Ignore the result in predeploy - it's all new anyway.
updateServerPlatform(predeployProperties);
// Update loggers and settings for the singleton logger and the session logger.
updateLoggers(predeployProperties);
//Bug5389828. Update the logging settings for the singleton logger.
initOrUpdateLogging(predeployProperties, AbstractSessionLog.getLog());
initOrUpdateLogging(predeployProperties, session.getSessionLog());
warnOldProperties(predeployProperties, session);
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, 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){
// build a list of entities the persistence unit represented by this EntityManagerSetupImpl will use
Collection entities = buildEntityList(privateClassLoader);
transformer = TransformerFactory.createTransformerAndModifyProject(session, entities, privateClassLoader);
}
state = STATE_PREDEPLOYED;
return transformer;
}
/**
* 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);
}
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 = 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.log(SessionLog.FINEST, SessionLog.PROPERTIES, "property_value_default", new Object[]{TopLinkProperties.SESSION_NAME, name});
}
session.setName(name);
}
/**
* Make any changes to our ServerSession that can be made after it is created.
* @param m
*/
protected void updateServerSession(Map m) {
if (session == null || session.isConnected()) {
return;
}
// Session name and ServerPlatform could've changed which will affect the loggers.
boolean serverPlatformChanged = updateServerPlatform(m);
boolean sessionNameChanged = updateSessionName(m);
if (serverPlatformChanged) {
// Update loggers and settings for the singleton logger and the session logger.
updateLoggers(m);
} 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);
}
initOrUpdateLogging(m, AbstractSessionLog.getLog());
initOrUpdateLogging(m, session.getSessionLog());
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);
// 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 = 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(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) {
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,
boolean enableLazyForOneToOne){
processor = new MetadataProcessor(persistenceUnitInfo, session, privateClassLoader, enableLazyForOneToOne);
// DO NOT CHANGE the order of invocation of various methods.
// build the list of mapping files and read them. Need to do this before
// we start processing entities as the list of entity classes
// depend on metadata read from mapping files.
boolean throwExceptionOnFail = "true".equalsIgnoreCase(
getConfigPropertyAsString(TOPLINK_ORM_THROW_EXCEPTIONS, "true"));
processor.readMappingFiles(throwExceptionOnFail);
processor.buildEntityList();
// process persistence unit metadata/defaults defined in
// ORM XML instance documents in the persistence unit
processor.processPersistenceUnitMetadata();
processor.processMappingFiles();
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;
}
}
}
}