org.eclipse.persistence.internal.sessions.DatabaseSessionImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* Copyright (c) 1998, 2020 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2020 IBM Corporation. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
// 14/05/2012-2.4 Guy Pelletier
// - 376603: Provide for table per tenant support for multitenant applications
// 22/05/2012-2.4 Guy Pelletier
// - 380008: Multitenant persistence units with a dedicated emf should force tenant property specification up front.
// 31/05/2012-2.4 Guy Pelletier
// - 381196: Multitenant persistence units with a dedicated emf should allow for DDL generation.
// 12/24/2012-2.5 Guy Pelletier
// - 389090: JPA 2.1 DDL Generation Support
// 01/11/2013-2.5 Guy Pelletier
// - 389090: JPA 2.1 DDL Generation Support
// 03/19/2015 - Rick Curtis
// - 462586 : Add national character support for z/OS.
// 04/14/2015 - Will Dazey
// - 464641 : Fixed platform matching returning CNF.
// 09/03/2015 - Will Dazey
// - 456067 : Added support for defining query timeout units
// 06/26/2018 - Will Dazey
// - 532160 : Add support for non-extension OracleXPlatform classes
package org.eclipse.persistence.internal.sessions;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.eclipse.persistence.config.PropertiesUtils;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.partitioning.PartitioningPolicy;
import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.exceptions.EclipseLinkException;
import org.eclipse.persistence.exceptions.IntegrityException;
import org.eclipse.persistence.exceptions.OptimisticLockException;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.internal.databaseaccess.Platform;
import org.eclipse.persistence.internal.helper.DBPlatformHelper;
import org.eclipse.persistence.internal.sequencing.Sequencing;
import org.eclipse.persistence.internal.sequencing.SequencingFactory;
import org.eclipse.persistence.internal.sequencing.SequencingHome;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.platform.database.DatabasePlatform;
import org.eclipse.persistence.platform.database.OraclePlatform;
import org.eclipse.persistence.platform.database.events.DatabaseEventListener;
import org.eclipse.persistence.platform.server.NoServerPlatform;
import org.eclipse.persistence.platform.server.ServerPlatform;
import org.eclipse.persistence.platform.server.ServerPlatformBase;
import org.eclipse.persistence.queries.AttributeGroup;
import org.eclipse.persistence.queries.DatabaseQuery;
import org.eclipse.persistence.queries.ObjectLevelReadQuery;
import org.eclipse.persistence.queries.QueryResultsCachePolicy;
import org.eclipse.persistence.queries.ReadQuery;
import org.eclipse.persistence.sequencing.Sequence;
import org.eclipse.persistence.sequencing.SequencingControl;
import org.eclipse.persistence.sessions.DatasourceLogin;
import org.eclipse.persistence.sessions.Login;
import org.eclipse.persistence.sessions.SessionProfiler;
import org.eclipse.persistence.tools.tuning.SessionTuner;
/**
* Implementation of org.eclipse.persistence.sessions.DatabaseSession
* The public interface should be used.
* @see org.eclipse.persistence.sessions.DatabaseSession
*
*
* Purpose: Define the implementation for a single user/single connection EclipseLink session.
*
* Description: The session is the primary interface into EclipseLink,
* the application should do all of its reading and writing of objects through the session.
* The session also manages transactions and units of work. The database session is intended
* for usage in two-tier client-server applications. Although it could be used in a server
* situation, it is limited to only having a single database connection and only allows
* a single open database transaction.
*
* Responsibilities:
*
* - Connecting/disconnecting.
*
- Reading and writing objects.
*
- Transaction and unit of work support.
*
- Identity maps and caching.
*
*/
public class DatabaseSessionImpl extends AbstractSession implements org.eclipse.persistence.sessions.DatabaseSession {
/**
* Database event listener, this allows database events to invalidate the cache.
*/
protected DatabaseEventListener databaseEventListener;
/**
* INTERNAL:
* sequencingHome for this session.
*/
protected SequencingHome sequencingHome;
/**
* Used to store the server platform that handles server-specific functionality for Oc4j, WLS, etc.
*/
protected ServerPlatform serverPlatform;
/**
* Stores the tuner used to tune the configuration of this session.
*/
protected SessionTuner tuner;
/**
* INTERNAL:
* connectedTime indicates the exact time this session was logged in.
*/
protected long connectedTime;
/**
* INTERNAL
* Indicate if this session is logged in.
*/
//Bug#3440544 Used to stop the attempt to login more than once.
protected volatile boolean isLoggedIn;
/**
* INTERNAL:
* Set the SequencingHome object used by the session.
*/
protected void setSequencingHome(SequencingHome sequencingHome) {
this.sequencingHome = sequencingHome;
}
/**
* INTERNAL:
* Return SequencingHome which used to obtain all sequence-related
* interfaces for DatabaseSession
*/
protected SequencingHome getSequencingHome() {
if (sequencingHome == null) {
setSequencingHome(SequencingFactory.createSequencingHome(this));
}
return sequencingHome;
}
/**
* INTERNAL:
* Return if the session was logged in.
* This may slight differ to isConnected which asks the JDBC Connection if it is connected.
*/
public boolean isLoggedIn() {
return isLoggedIn;
}
/**
* Return the database event listener, this allows database events to invalidate the cache.
*/
@Override
public DatabaseEventListener getDatabaseEventListener() {
return databaseEventListener;
}
/**
* PUBLIC:
* Set the database event listener, this allows database events to invalidate the cache.
*/
@Override
public void setDatabaseEventListener(DatabaseEventListener databaseEventListener) {
this.databaseEventListener = databaseEventListener;
}
/**
* INTERNAL:
* Issue any pre connect and post connect without an actual connection to
* the database. Descriptors are initialized in postConnectDatasource and
* are used in DDL generation. This will look to set the schema platform
* via the JPA 2.1 properties or through a detection on the connection
* before DDL generation.
*/
public void setDatasourceAndInitialize() throws DatabaseException {
preConnectDatasource();
setOrDetectDatasource(false);
postConnectDatasource();
}
/**
* INTERNAL:
* Will set the platform from specified schema generation properties or
* by detecting it through the connection (if one is available).
* Any connection that is open for detection is closed before this method
* returns.
*
* @param throwException - set to true if the caller cares to throw exceptions, false to swallow them.
*/
protected void setOrDetectDatasource(boolean throwException) {
String vendorName = null;
String minorVersion = null;
String majorVersion = null;
String driverName = null;
// Try to set the platform from JPA 2.1 schema properties first before
// attempting a detection.
if (getProperties().containsKey(PersistenceUnitProperties.SCHEMA_DATABASE_PRODUCT_NAME)) {
vendorName = (String) getProperties().get(PersistenceUnitProperties.SCHEMA_DATABASE_PRODUCT_NAME);
minorVersion = (String) getProperties().get(PersistenceUnitProperties.SCHEMA_DATABASE_MINOR_VERSION);
majorVersion = (String) getProperties().get(PersistenceUnitProperties.SCHEMA_DATABASE_MAJOR_VERSION);
} else {
Connection conn = null;
try {
conn = (Connection) getReadLogin().connectToDatasource(null, this);
DatabaseMetaData dmd = conn.getMetaData();
vendorName = dmd.getDatabaseProductName();
minorVersion = dmd.getDatabaseProductVersion();
majorVersion = Integer.toString(dmd.getDatabaseMajorVersion());
driverName = conn.getMetaData().getDriverName();
} catch (SQLException ex) {
if (throwException) {
DatabaseException dbEx = DatabaseException.errorRetrieveDbMetadataThroughJDBCConnection();
// Typically exception would occur if user did not provide
// correct connection
// parameters. The root cause of exception should be
// propagated up
dbEx.initCause(ex);
throw dbEx;
}
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException ex) {
if (throwException) {
DatabaseException dbEx = DatabaseException.errorRetrieveDbMetadataThroughJDBCConnection();
// Typically exception would occur if user did not
// provide correct connection
// parameters. The root cause of exception should be
// propagated up
dbEx.initCause(ex);
throw dbEx;
}
}
}
}
}
String platformName = null;
try {
// null out the cached platform because the platform on the login
// will be changed by the following line of code
this.platform = null;
platformName = DBPlatformHelper.getDBPlatform(vendorName, minorVersion, majorVersion, getSessionLog());
getLogin().setPlatformClassName(platformName);
} catch (EclipseLinkException classNotFound) {
if (platformName != null && platformName.indexOf("Oracle") != -1) {
try {
// If we are running against Oracle, it is possible that we are
// running in an environment where the extension OracleXPlatform classes can
// not be loaded. Try using the core OracleXPlatform classes
platformName = DBPlatformHelper.getDBPlatform("core."+ vendorName, minorVersion, majorVersion, getSessionLog());
getLogin().setPlatformClassName(platformName);
} catch (EclipseLinkException oracleClassNotFound) {
// If we still cannot classload a matching OracleXPlatform class,
// fallback on the base OraclePlatform class
getLogin().setPlatformClassName(OraclePlatform.class.getName());
}
} else {
throw classNotFound;
}
}
if (driverName != null) {
getLogin().getPlatform().setDriverName(driverName);
}
}
/**
* PUBLIC:
* Return SequencingControl which used for sequencing setup and
* customization including management of sequencing preallocation.
*/
@Override
public SequencingControl getSequencingControl() {
return getSequencingHome().getSequencingControl();
}
/**
* PUBLIC:
* Return the Sequencing object used by the session.
*/
@Override
public Sequencing getSequencing() {
return getSequencingHome().getSequencing();
}
/**
* INTERNAL:
* Indicates whether SequencingCallback is required.
* Always returns false if sequencing is not connected.
*/
public boolean isSequencingCallbackRequired() {
return getSequencingHome().isSequencingCallbackRequired();
}
/**
* INTERNAL:
* Creates sequencing object
*/
public void initializeSequencing() {
getSequencingHome().onDisconnect();
getSequencingHome().onConnect();
}
/**
* INTERNAL:
* If sequencing is connected then initializes sequences referenced by the passed descriptors,
* otherwise connects sequencing.
*/
public void addDescriptorsToSequencing(Collection descriptors) {
getSequencingHome().onAddDescriptors(descriptors);
}
/**
* INTERNAL:
* Called in the end of beforeCompletion of external transaction synchronization listener.
* Close the managed sql connection corresponding to the external transaction.
*/
@Override
public void releaseJTSConnection() {
getAccessor().closeJTSConnection();
}
/**
* INTERNAL:
* Create and return a new default database session.
* Used for EJB SessionManager to instantiate a database session
*/
public DatabaseSessionImpl() {
super();
this.setServerPlatform(new NoServerPlatform(this));
this.shouldOptimizeResultSetAccess = ObjectLevelReadQuery.isResultSetAccessOptimizedQueryDefault;
}
/**
* PUBLIC:
* Create and return a new session.
* By giving the login information on creation this allows the session to initialize itself
* to the platform given in the login. This constructor does not return a connected session.
* To connect the session to the database login() must be sent to it. The login(userName, password)
* method may also be used to connect the session, this allows for the user name and password
* to be given at login but for the other database information to be provided when the session is created.
*/
public DatabaseSessionImpl(Login login) {
this(new org.eclipse.persistence.sessions.Project(login));
}
/**
* PUBLIC:
* Create and return a new session.
* This constructor does not return a connected session.
* To connect the session to the database login() must be sent to it. The login(userName, password)
* method may also be used to connect the session, this allows for the user name and password
* to be given at login but for the other database information to be provided when the session is created.
*/
public DatabaseSessionImpl(org.eclipse.persistence.sessions.Project project) {
super(project);
this.setServerPlatform(new NoServerPlatform(this));
this.shouldOptimizeResultSetAccess = ObjectLevelReadQuery.isResultSetAccessOptimizedQueryDefault;
}
/**
* PUBLIC:
* Add the descriptor to the session.
* All persistent classes must have a descriptor registered for them with the session.
* It is best to add the descriptors before login, if added after login the order in which
* descriptors are added is dependent on inheritance and references unless the addDescriptors
* method is used.
*
* @see #addDescriptors(Vector)
* @see #addDescriptors(org.eclipse.persistence.sessions.Project)
*/
@Override
public void addDescriptor(ClassDescriptor descriptor) {
// Reset cached data, as may be invalid later on.
this.lastDescriptorAccessed = null;
// Bug# 429760: Add descriptor to the session when session Map exists and is not the same as in the project.
if (this.descriptors != null && this.descriptors != getProject().getDescriptors()) {
this.descriptors.put(descriptor.getJavaClass(), descriptor);
}
getProject().addDescriptor(descriptor, this);
}
/**
* PUBLIC:
* Add the descriptors to the session.
* All persistent classes must have a descriptor registered for them with the session.
* This method allows for a batch of descriptors to be added at once so that EclipseLink
* can resolve the dependencies between the descriptors and perform initialization optimally.
* @param descriptors The descriptors to be added to the session and project.
*/
@Override
public void addDescriptors(final Collection descriptors) {
// Reset cached data, as may be invalid later on.
this.lastDescriptorAccessed = null;
// Bug# 429760: Add descriptors to the session when session Map exists and is not the same as in the project.
if (this.descriptors != null && this.descriptors != getProject().getDescriptors()) {
for (ClassDescriptor descriptor : (Collection) descriptors) {
this.descriptors.put(descriptor.getJavaClass(), descriptor);
}
}
getProject().addDescriptors(descriptors, this);
}
/**
* PUBLIC:
* Add the descriptors to the session from the Project.
* This can be used to combine the descriptors from multiple projects into a single session.
* This can be called after the session has been connected as long as there are no external dependencies.
*/
@Override
public void addDescriptors(org.eclipse.persistence.sessions.Project project) {
// Reset cached data, as may be invalid later on.
this.lastDescriptorAccessed = null;
getProject().addDescriptors(project, this);
}
/**
* PUBLIC:
* Add the sequence to the session.
* Allows to add a new sequence to the session even if the session is connected.
* If the session is connected then the sequence is added only
* if there is no sequence with the same name already in use.
* Call this method before addDescriptor(s) if need to add new descriptor
* with a new non-default sequence to connected session.
*
* @see #addSequences(Collection)
*/
@Override
public void addSequence(Sequence sequence) {
getProject().getLogin().getDatasourcePlatform().addSequence(sequence, this.getSequencingHome().isConnected());
}
/**
* INTERNAL:
* Connect the session only.
*/
public void connect() throws DatabaseException {
getAccessor().connect(getDatasourceLogin(), this);
}
/**
* INTERNAL:
* Disconnect the accessor only.
*/
public void disconnect() throws DatabaseException {
getSequencingHome().onDisconnect();
getAccessor().disconnect(this);
}
/**
* PUBLIC:
* Answer the server platform to handle server specific behavior for WLS, Oc4j, etc.
*
* If the user wants a different external transaction controller class or
* to provide some different behavior than the provided ServerPlatform(s), we recommend
* subclassing org.eclipse.persistence.platform.server.ServerPlatformBase (or a subclass),
* and overriding:
*
* ServerPlatformBase.getExternalTransactionControllerClass()
* ServerPlatformBase.registerMBean()
* ServerPlatformBase.unregisterMBean()
*
* for the desired behavior.
*
* @see org.eclipse.persistence.platform.server.ServerPlatformBase
*/
@Override
public ServerPlatform getServerPlatform() {
return serverPlatform;
}
/**
* PUBLIC:
* Set the server platform to handle server specific behavior for WLS, Oc4j, etc
*
* This is not permitted after the session is logged in.
*
* If the user wants a different external transaction controller class or
* to provide some different behavior than the provided ServerPlatform(s), we recommend
* subclassing org.eclipse.persistence.platform.server.ServerPlatformBase (or a subclass),
* and overriding:
*
* ServerPlatformBase.getExternalTransactionControllerClass()
* ServerPlatformBase.registerMBean()
* ServerPlatformBase.unregisterMBean()
*
* for the desired behavior.
*
* @see org.eclipse.persistence.platform.server.ServerPlatformBase
*/
@Override
public void setServerPlatform(ServerPlatform newServerPlatform) {
if (this.isLoggedIn) {
throw ValidationException.serverPlatformIsReadOnlyAfterLogin(newServerPlatform.getClass().getName());
}
this.serverPlatform = newServerPlatform;
}
/**
* INTERNAL:
* Logout in case still connected.
*/
@Override
protected void finalize() throws DatabaseException {
if (isConnected()) {
logout();
}
}
/**
* INTERNAL:
* Return the database platform currently connected to.
* The platform is used for database specific behavior.
* NOTE: this must only be used for relational specific usage,
* it will fail for non-relational datasources.
*/
@Override
public DatabasePlatform getPlatform() {
// PERF: Cache the platform.
if (platform == null) {
if(isLoggedIn) {
platform = getDatasourceLogin().getPlatform();
} else {
return getDatasourceLogin().getPlatform();
}
}
return (DatabasePlatform)platform;
}
/**
* INTERNAL:
* Return the database platform currently connected to.
* The platform is used for database specific behavior.
*/
@Override
public Platform getDatasourcePlatform() {
// PERF: Cache the platform.
if (platform == null) {
if(isLoggedIn) {
platform = getDatasourceLogin().getDatasourcePlatform();
} else {
return getDatasourceLogin().getDatasourcePlatform();
}
}
return platform;
}
/**
* INTERNAL:
* Return the database platform currently connected to
* for specified class.
* The platform is used for database specific behavior.
*/
@Override
public Platform getPlatform(Class domainClass) {
// PERF: Cache the platform.
if (platform == null) {
if(isLoggedIn) {
platform = getDatasourceLogin().getDatasourcePlatform();
} else {
return getDatasourceLogin().getDatasourcePlatform();
}
}
return platform;
}
/**
* INTERNAL:
* A descriptor may have been added after the session is logged in.
* In this case the descriptor must be allowed to initialize any dependencies on this session.
* Normally the descriptors are added before login, then initialized on login.
*/
public void initializeDescriptorIfSessionAlive(ClassDescriptor descriptor) {
if (isConnected() && (descriptor.requiresInitialization(this))) {
try {
try {
Collection descriptorsToAdd = new ArrayList(1);
descriptorsToAdd.add(descriptor);
addDescriptorsToSequencing(descriptorsToAdd);
descriptor.preInitialize(this);
descriptor.initialize(this);
descriptor.postInitialize(this);
getCommitManager().initializeCommitOrder();
} catch (RuntimeException exception) {
getIntegrityChecker().handleError(exception);
}
if (getIntegrityChecker().hasErrors()) {
//CR#4011
handleException(new IntegrityException(getIntegrityChecker()));
}
} finally {
clearIntegrityChecker();
}
}
}
/**
* INTERNAL:
* Allow each descriptor to initialize any dependencies on this session.
* This is done in two passes to allow the inheritance to be resolved first.
* Normally the descriptors are added before login, then initialized on login.
*/
public void initializeDescriptors() {
// Must clone to avoid modification of the map while enumerating.
initializeDescriptors((Map)((HashMap)getDescriptors()).clone(), true);
// Initialize serializer
if (this.serializer != null) {
this.serializer.initialize(null, null, this);
}
// Initialize partitioning policies.
for (PartitioningPolicy policy : getProject().getPartitioningPolicies().values()) {
policy.initialize(this);
}
if (getProject().getMultitenantPolicy() != null) {
getProject().getMultitenantPolicy().initialize(this);
}
// Process JPA named queries and add as session queries,
// this must be done after descriptor init as requires to parse the JPQL.
processJPAQueries();
// Configure default query cache for all named queries.
QueryResultsCachePolicy defaultQueryCachePolicy = getProject().getDefaultQueryResultsCachePolicy();
if (defaultQueryCachePolicy != null) {
for (List queries : getQueries().values()) {
for (DatabaseQuery query : queries) {
if (query.isReadQuery() && (query.getDescriptor() != null) && !query.getDescriptor().getCachePolicy().isIsolated()) {
ReadQuery readQuery = (ReadQuery)query;
if (!readQuery.shouldCacheQueryResults()) {
readQuery.setQueryResultsCachePolicy(defaultQueryCachePolicy.clone());
}
}
}
}
}
for (AttributeGroup group : getProject().getAttributeGroups().values()){
getAttributeGroups().put(group.getName(), group);
this.getDescriptor(group.getType()).addAttributeGroup(group);
}
}
/**
* INTERNAL:
* Allow each descriptor to initialize any dependencies on this session.
* This is done in two passes to allow the inheritance to be resolved first.
* Normally the descriptors are added before login, then initialized on login.
* The descriptors session must be used, not the broker.
* Sequencing is (re)initialized: disconnected (if has been already connected), then connected.
*/
public void initializeDescriptors(Map descriptors) {
initializeDescriptors(descriptors.values(), false);
}
public void initializeDescriptors(Collection descriptors) {
initializeDescriptors(descriptors, false);
}
/**
* INTERNAL:
* Allow each descriptor to initialize any dependencies on this session.
* This is done in two passes to allow the inheritance to be resolved first.
* Normally the descriptors are added before login, then initialized on login.
* The descriptors session must be used, not the broker.
* If shouldInitializeSequencing parameter is true then sequencing is (re)initialized:
* disconnected (if has been connected), then connected.
* If shouldInitializeSequencing parameter is false then
* if sequencing has been already connected, then it stays connected:
* only the new sequences used by the passed descriptors are initialized;
* otherwise, if sequencing has NOT been connected then it is connected
* (just like in shouldInitializeSequencing==true case);
* disconnected (if has been connected), then connected.
*/
public void initializeDescriptors(Map descriptors, boolean shouldInitializeSequencing) {
initializeDescriptors(descriptors.values(), shouldInitializeSequencing);
}
public void initializeDescriptors(Collection descriptors, boolean shouldInitializeSequencing) {
if (shouldInitializeSequencing) {
initializeSequencing();
} else {
addDescriptorsToSequencing(descriptors);
}
try {
// First initialize basic properties (things that do not depend on anything else)
Iterator iterator = descriptors.iterator();
while (iterator.hasNext()) {
ClassDescriptor descriptor = (ClassDescriptor)iterator.next();
try {
AbstractSession session = getSessionForClass(descriptor.getJavaClass());
if (descriptor.requiresInitialization(session)) {
descriptor.preInitialize(session);
} else if (descriptor.hasTablePerMultitenantPolicy()) {
// If the descriptor doesn't require initialization and
// has a table per tenant policy then add to the list
// to be cloned and initialized per client session.
addTablePerTenantDescriptor(descriptor);
}
//check if inheritance is involved in aggregate relationship, and let the parent know the child descriptor
if (descriptor.isDescriptorTypeAggregate() && descriptor.isChildDescriptor()) {
descriptor.initializeAggregateInheritancePolicy(session);
}
} catch (RuntimeException exception) {
getIntegrityChecker().handleError(exception);
}
}
// Second initialize basic mappings
iterator = descriptors.iterator();
while (iterator.hasNext()) {
ClassDescriptor descriptor = (ClassDescriptor)iterator.next();
try {
AbstractSession session = getSessionForClass(descriptor.getJavaClass());
if (descriptor.requiresInitialization(session)) {
descriptor.initialize(session);
}
} catch (RuntimeException exception) {
getIntegrityChecker().handleError(exception);
}
}
// Third initialize child dependencies
iterator = descriptors.iterator();
while (iterator.hasNext()) {
ClassDescriptor descriptor = (ClassDescriptor)iterator.next();
try {
AbstractSession session = getSessionForClass(descriptor.getJavaClass());
if (descriptor.requiresInitialization(session)) {
descriptor.postInitialize(session);
}
} catch (RuntimeException exception) {
getIntegrityChecker().handleError(exception);
}
}
if (getIntegrityChecker().hasErrors()) {
//CR#4011
handleSevere(new IntegrityException(getIntegrityChecker()));
}
} finally {
clearIntegrityChecker();
}
getCommitManager().initializeCommitOrder();
}
/**
* INTERNAL:
* Return if this session is a database session.
*/
@Override
public boolean isDatabaseSession() {
return true;
}
/**
* PUBLIC:
* Returns true if Protected Entities should be built within this session
*/
@Override
public boolean isProtectedSession(){
return false;
}
/**
* INTERNAL:
* Return the login for the read connection. Used by the platform autodetect feature
*/
protected Login getReadLogin(){
return getDatasourceLogin();
}
/**
* PUBLIC:
* Connect to the database using the predefined login.
* During connection, attempt to auto detect the required database platform.
* This method can be used in systems where for ease of use developers have
* EclipseLink autodetect the platform.
* To be safe, however, the platform should be configured directly.
* The login must have been assigned when or after creating the session.
*
*/
public void loginAndDetectDatasource() throws DatabaseException {
preConnectDatasource();
setOrDetectDatasource(true);
connect();
postConnectDatasource();
}
/**
* PUBLIC:
* Connect to the database using the predefined login.
* The login must have been assigned when or after creating the session.
*
* @see #login(Login)
*/
@Override
public void login() throws DatabaseException {
preConnectDatasource();
connect();
postConnectDatasource();
}
/**
* INTERNAL:
* This method includes all of the code that is issued before the datasource
* is connected to.
*/
protected void preConnectDatasource(){
//Bug#3440544 Check if logged in already to stop the attempt to login more than once
if (isLoggedIn) {
throw ValidationException.alreadyLoggedIn(this.getName());
}
this.platform = null;
if (isInProfile()) {
getProfiler().initialize();
}
updateProfile(SessionProfiler.LoginTime, new Date(System.currentTimeMillis()));
updateProfile(SessionProfiler.SessionName, getName());
// Login and initialize
if (this.eventManager != null) {
this.eventManager.preLogin(this);
}
if (!hasBroker()) {
//setup the external transaction controller
getServerPlatform().initializeExternalTransactionController();
log(SessionLog.INFO, null, "topLink_version", DatasourceLogin.getVersion());
if (getServerPlatform().getServerNameAndVersion() != null &&
!getServerPlatform().getServerNameAndVersion().equals(ServerPlatformBase.DEFAULT_SERVER_NAME_AND_VERSION)) {
log(SessionLog.INFO, null, "application_server_name_and_version", getServerPlatform().getServerNameAndVersion());
}
}
this.isLoggingOff = (getLogLevel() == SessionLog.OFF);
}
/**
* INTERNAL:
* This method includes all of the code that is issued after the datasource
* is connected to.
*/
protected void postConnectDatasource(){
// Initialize the Platform properties now that the datasource is connected and the Platform should be initialized
if((getDatasourcePlatform() instanceof DatabasePlatform)) {
final Platform platform = getDatasourcePlatform();
String platformValues = (String) getProperties().get(PersistenceUnitProperties.TARGET_DATABASE_PROPERTIES);
PropertiesUtils.set(platform, PersistenceUnitProperties.TARGET_DATABASE_PROPERTIES, platformValues);
}
if (!hasBroker()) {
initializeDescriptors();
//added to process ejbQL query strings
if (getCommandManager() != null) {
getCommandManager().initialize();
}
}
// Once the descriptors are initialized we can check if there are
// multitenant entities and if this session (emf) is shared or not. If
// not shared, all multitenant properties must be available and set by
// the user at this point for us to validate (meaning they must be set
// in a persitence.xml or passed into the create EMF call).
if (getProperties().containsKey(PersistenceUnitProperties.MULTITENANT_SHARED_EMF)) {
String value = (String) getProperties().get(PersistenceUnitProperties.MULTITENANT_SHARED_EMF);
if (!Boolean.valueOf(value)) {
for (String property : getMultitenantContextProperties()) {
if (! getProperties().containsKey(property)) {
throw ValidationException.multitenantContextPropertyForNonSharedEMFNotSpecified(property);
}
}
// Once the properties are validated we can allow ddl generation to happen (if needed).
project.setAllowTablePerMultitenantDDLGeneration(true);
}
}
log(SessionLog.FINE, SessionLog.CONNECTION, "login_successful", this.getName());
// postLogin event should not be risen before descriptors have been initialized
if (!hasBroker()) {
postLogin();
}
initializeConnectedTime();
this.isLoggedIn = true;
this.platform = null;
if (!hasBroker()) {
//register the MBean
getServerPlatform().registerMBean();
}
this.descriptors = getDescriptors();
if (!isBroker()) {
// EclipseLink 23869 - Initialize plaformOperators eagerly to avoid concurrency issues.
getDatasourcePlatform().initialize();
getIdentityMapAccessorInstance().getIdentityMapManager().checkIsCacheAccessPreCheckRequired();
}
if (this.databaseEventListener != null) {
this.databaseEventListener.register(this);
}
if ((getDatasourcePlatform() instanceof DatabasePlatform) && getPlatform().getBatchWritingMechanism() != null) {
getPlatform().getBatchWritingMechanism().initialize(this);
}
}
/**
* INTERNAL:
* Rise postLogin event.
*/
public void postLogin() {
if (this.eventManager != null) {
this.eventManager.postLogin(this);
}
}
/**
* PUBLIC:
* Connect to the database using the given user name and password.
* The additional login information must have been preset in the session's login attribute.
* This is the login that should be used if each user has their own id,
* but all users share the same database configuration.
*/
@Override
public void login(String userName, String password) throws DatabaseException {
getDatasourceLogin().setUserName(userName);
getDatasourceLogin().setPassword(password);
login();
}
/**
* PUBLIC:
* Connect to the database using the given login.
* The login may also the preset and the login() protocol called.
* This is the login should only be used if each user has their own database configuration.
*/
@Override
public void login(Login login) throws DatabaseException {
setLogin(login);
login();
}
/**
* PUBLIC:
* Disconnect from the database.
*
* @exception EclipseLinkException if a transaction is active, you must rollback any active transaction before logout.
* @exception DatabaseException the database will also raise an error if their is an active transaction,
* or a general error occurs.
*/
@Override
public void logout() throws DatabaseException {
if (this.eventManager != null) {
this.eventManager.preLogout(this);
}
cleanUpInjectionManager();
// Reset cached data, as may be invalid later on.
this.lastDescriptorAccessed = null;
if (isInTransaction()) {
throw DatabaseException.logoutWhileTransactionInProgress();
}
if (getAccessor() == null) {
return;
}
if (this.databaseEventListener != null) {
this.databaseEventListener.remove(this);
}
// We're logging out so turn off change propagation.
setShouldPropagateChanges(false);
if (!hasBroker()) {
if (getCommandManager() != null) {
getCommandManager().shutdown();
}
// Unregister the JMX MBean before logout to avoid a javax.naming.NameNotFoundException
getServerPlatform().shutdown();
}
disconnect();
getIdentityMapAccessor().initializeIdentityMaps();
this.isLoggedIn = false;
if (this.eventManager != null) {
this.eventManager.postLogout(this);
}
log(SessionLog.FINE, SessionLog.CONNECTION, "logout_successful", this.getName());
}
/**
* PUBLIC:
* Initialize the time that this session got connected. This can help determine how long a session has been
* connected.
*/
public void initializeConnectedTime() {
connectedTime = System.currentTimeMillis();
}
/**
* PUBLIC:
* Answer the time that this session got connected. This can help determine how long a session has been
* connected.
*/
public long getConnectedTime() {
return connectedTime;
}
/**
* PUBLIC:
* Write all of the objects and all of their privately owned parts in the database.
* The objects will be committed through a single transaction.
*
* @exception DatabaseException if an error occurs on the database,
* these include constraint violations, security violations and general database errors.
* @exception OptimisticLockException if the object's descriptor is using optimistic locking and
* the object has been updated or deleted by another user since it was last read.
*/
@Override
public void writeAllObjects(Collection domainObjects) throws DatabaseException, OptimisticLockException {
for (Iterator objectsEnum = domainObjects.iterator(); objectsEnum.hasNext();) {
writeObject(objectsEnum.next());
}
}
/**
* PUBLIC:
* Write all of the objects and all of their privately owned parts in the database.
* The objects will be committed through a single transaction.
*
* @exception DatabaseException if an error occurs on the database,
* these include constraint violations, security violations and general database errors.
* @exception OptimisticLockException if the object's descriptor is using optimistic locking and
* the object has been updated or deleted by another user since it was last read.
*/
public void writeAllObjects(Vector domainObjects) throws DatabaseException, OptimisticLockException {
for (Enumeration objectsEnum = domainObjects.elements(); objectsEnum.hasMoreElements();) {
writeObject(objectsEnum.nextElement());
}
}
/**
* INTERNAL:
* A query execution failed due to an invalid query.
* Re-connect and retry the query.
*/
@Override
public Object retryQuery(DatabaseQuery query, AbstractRecord row, DatabaseException databaseException, int retryCount, AbstractSession executionSession) {
if (getClass() != DatabaseSessionImpl.class) {
return super.retryQuery(query, row, databaseException, retryCount, executionSession);
}
//attempt to reconnect connection:
final int count = getLogin().getQueryRetryAttemptCount();
while (retryCount < count) {
try {
// if database session then re-establish connection
// else the session will just get a new
// connection from the pool
++retryCount;
databaseException.getAccessor().reestablishConnection(this);
break;
} catch (DatabaseException ex) {
// failed to get connection because of
// database error.
try {
// Give the failover time to recover.
Thread.currentThread().sleep(getLogin().getDelayBetweenConnectionAttempts());
Object[] args = new Object[1];
args[0] = ex;
log(SessionLog.INFO, SessionLog.QUERY, "communication_failure_attempting_query_retry", args, null);
} catch (InterruptedException intEx) {
break;
}
}
}
return executionSession.executeQuery(query, row, retryCount);
}
/**
* Return the tuner used to tune the configuration of this session.
*/
public SessionTuner getTuner() {
return tuner;
}
/**
* Set the tuner used to tune the configuration of this session.
*/
public void setTuner(SessionTuner tuner) {
this.tuner = tuner;
}
}