All Downloads are FREE. Search and download functionalities are using the official Maven repository.

oracle.toplink.essentials.internal.sessions.DatabaseSessionImpl Maven / Gradle / Ivy

There is a newer version: 2.1-60f
Show newest version
/*
 * 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.sessions;

import java.sql.Connection;
import java.sql.SQLException;

import java.util.*;

import oracle.toplink.essentials.descriptors.ClassDescriptor;
import oracle.toplink.essentials.exceptions.*;
import oracle.toplink.essentials.internal.helper.DBPlatformHelper;
import oracle.toplink.essentials.internal.databaseaccess.Platform;
import oracle.toplink.essentials.internal.databaseaccess.DatabasePlatform;
import oracle.toplink.essentials.internal.sequencing.Sequencing;
import oracle.toplink.essentials.internal.sequencing.SequencingCallback;
import oracle.toplink.essentials.internal.sequencing.SequencingHome;
import oracle.toplink.essentials.internal.sequencing.SequencingFactory;
import oracle.toplink.essentials.sequencing.SequencingControl;
import oracle.toplink.essentials.logging.SessionLog;
import oracle.toplink.essentials.sessions.Login;
import oracle.toplink.essentials.sessions.DatasourceLogin;
import oracle.toplink.essentials.sessions.SessionProfiler;
import oracle.toplink.essentials.platform.server.ServerPlatform;
import oracle.toplink.essentials.platform.server.NoServerPlatform;

/**
 * Implementation of oracle.toplink.essentials.sessions.DatabaseSession
 * The public interface should be used by public API, the implementation should be used internally.
 * @see oracle.toplink.essentials.sessions.DatabaseSession
 *
 * 

* Purpose: Define the implementation for a single user/single connection TopLink session. *

* Description: The session is the primary interface into TopLink, * 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 limitted 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 oracle.toplink.essentials.sessions.DatabaseSession { /** * INTERNAL: * sequencingHome for this session. */ private SequencingHome sequencingHome; /** * Used to store the server platform that handles server-specific functionality for Oc4j, WLS, etc. */ private ServerPlatform serverPlatform; /** * INTERNAL: * connectedTime indicates the exact time this session was logged in. */ private long connectedTime; /** * INTERNAL * Indicate if this session is logged in. */ //Bug#3440544 Used to stop the attempt to login more than once. protected 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; } /** * PUBLIC: * Return SequencingControl which used for sequencing setup and * customization including management of sequencing preallocation. */ public SequencingControl getSequencingControl() { return getSequencingHome().getSequencingControl(); } /** * INTERNAL: * Return the Sequencing object used by the session. */ public Sequencing getSequencing() { return getSequencingHome().getSequencing(); } /** * INTERNAL: * Creates sequencing object */ public void initializeSequencing() { getSequencingHome().onDisconnect(); getSequencingHome().onConnect(); } /** * INTERNAL: * Called after transaction is completed (committed or rolled back) */ public void afterTransaction(boolean committed, boolean isExternalTransaction) { SequencingCallback callback = getSequencingHome().getSequencingCallback(); if (callback != null) { callback.afterTransaction(getAccessor(), committed); } } /** * 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)); } /** * 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 oracle.toplink.essentials.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(oracle.toplink.essentials.sessions.Project project) { super(project); this.setServerPlatform(new NoServerPlatform(this)); } /** * 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 dependant on inheritice and references unless the addDescriptors * method is used. * * @see #addDescriptors(Vector) * @see #addDescriptors(oracle.toplink.essentials.sessions.Project) */ public void addDescriptor(ClassDescriptor descriptor) { // Reset cached data, as may be invalid later on. this.lastDescriptorAccessed = null; 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 TopLink * can resolve the dependancies between the descriptors and perform initialization optimially. */ public void addDescriptors(Vector descriptors) { // Reset cached data, as may be invalid later on. this.lastDescriptorAccessed = null; 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. */ public void addDescriptors(oracle.toplink.essentials.sessions.Project project) { // Reset cached data, as may be invalid later on. this.lastDescriptorAccessed = null; getProject().addDescriptors(project, this); } /** * 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 behaviour for WLS, Oc4j, etc. * * If the user wants a different external transaction controller class or * to provide some different behaviour than the provided ServerPlatform(s), we recommend * subclassing oracle.toplink.essentials.platform.server.ServerPlatformBase (or a subclass), * and overriding: * * ServerPlatformBase.getExternalTransactionControllerClass() * ServerPlatformBase.registerMBean() * ServerPlatformBase.unregisterMBean() * * for the desired behaviour. * * @see oracle.toplink.essentials.platform.server.ServerPlatformBase */ public ServerPlatform getServerPlatform() { return serverPlatform; } /** * PUBLIC: * Set the server platform to handle server specific behaviour 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 behaviour than the provided ServerPlatform(s), we recommend * subclassing oracle.toplink.essentials.platform.server.ServerPlatformBase (or a subclass), * and overriding: * * ServerPlatformBase.getExternalTransactionControllerClass() * ServerPlatformBase.registerMBean() * ServerPlatformBase.unregisterMBean() * * for the desired behaviour. * * @see oracle.toplink.essentials.platform.server.ServerPlatformBase */ public void setServerPlatform(ServerPlatform newServerPlatform) { if (this.isConnected()) { throw ValidationException.serverPlatformIsReadOnlyAfterLogin(newServerPlatform.getClass().getName()); } this.serverPlatform = newServerPlatform; } /** * INTERNAL: * Logout in case still connected. */ protected void finalize() throws DatabaseException { if (isConnected()) { logout(); } } /** * PUBLIC: * Return all registered descriptors. */ public Map getDescriptors() { return getProject().getDescriptors(); } /** * 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. */ 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. */ public Platform getDatasourcePlatform() { // 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 dependancies on this session. * Normally the descriptors are added before login, then initialized on login. */ public void initializeDescriptorIfSessionAlive(ClassDescriptor descriptor) { if (isConnected() && (descriptor.requiresInitialization())) { try { try { 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 dependancies on this session. * This is done in two passes to allow the inheritence to be resolved first. * Normally the descriptors are added before login, then initialized on login. */ public void initializeDescriptors() { // Assume all descriptors are CMP, if any are not their init will set this to false. getProject().setIsPureCMP2Project(true); // Must clone to avoid modification of the hashtable while enumerating. initializeDescriptors((Map)((HashMap)getDescriptors()).clone()); } /** * INTERNAL: * Allow each descriptor to initialize any dependancies on this session. * This is done in two passes to allow the inheritence to be resolved first. * Normally the descriptors are added before login, then initialized on login. * The descriptors session must be used, not the broker. */ public void initializeDescriptors(Map descriptors) { initializeSequencing(); try { // First initialize basic properties (things that do not depend on anything else) Iterator iterator = descriptors.values().iterator(); while (iterator.hasNext()) { ClassDescriptor descriptor = (ClassDescriptor)iterator.next(); try { AbstractSession session = getSessionForClass(descriptor.getJavaClass()); if (descriptor.requiresInitialization()) { descriptor.preInitialize(session); } //check if inheritance is involved in aggregate relationship, and let the parent know the child descriptor if (descriptor.isAggregateDescriptor() && descriptor.isChildDescriptor()) { descriptor.initializeAggregateInheritancePolicy(session); } } catch (RuntimeException exception) { getIntegrityChecker().handleError(exception); } } // Second basic initialize mappings iterator = descriptors.values().iterator(); while (iterator.hasNext()) { ClassDescriptor descriptor = (ClassDescriptor)iterator.next(); try { AbstractSession session = getSessionForClass(descriptor.getJavaClass()); if (descriptor.requiresInitialization()) { descriptor.initialize(session); } } catch (RuntimeException exception) { getIntegrityChecker().handleError(exception); } } // Third initialize child dependencies iterator = descriptors.values().iterator(); while (iterator.hasNext()) { ClassDescriptor descriptor = (ClassDescriptor)iterator.next(); try { AbstractSession session = getSessionForClass(descriptor.getJavaClass()); if (descriptor.requiresInitialization()) { descriptor.postInitialize(session); } } catch (RuntimeException exception) { getIntegrityChecker().handleError(exception); } } try { getCommitManager().initializeCommitOrder(); } catch (RuntimeException exception) { getIntegrityChecker().handleError(exception); } if (getIntegrityChecker().hasErrors()) { //CR#4011 handleSevere(new IntegrityException(getIntegrityChecker())); } } finally { clearIntegrityChecker(); } } /** * INTERNAL: * Allow each descriptor to initialize any dependancies on this session. * This is done in two passes to allow the inheritence to be resolved first. * Normally the descriptors are added before login, then initialized on login. * The descriptors session must be used, not the broker. */ public void initializeDescriptors(Vector descriptors) { initializeSequencing(); try { // First initialize basic properties (things that do not depend on anything else) for (Enumeration descriptorEnum = descriptors.elements(); descriptorEnum.hasMoreElements();) { try { ClassDescriptor descriptor = (ClassDescriptor)descriptorEnum.nextElement(); AbstractSession session = getSessionForClass(descriptor.getJavaClass()); if (descriptor.requiresInitialization()) { descriptor.preInitialize(session); } //check if inheritance is involved in aggregate relationship, and let the parent know the child descriptor if (descriptor.isAggregateDescriptor() && descriptor.isChildDescriptor()) { descriptor.initializeAggregateInheritancePolicy(session); } } catch (RuntimeException exception) { getIntegrityChecker().handleError(exception); } } // Second basic initialize mappings for (Enumeration descriptorEnum = descriptors.elements(); descriptorEnum.hasMoreElements();) { try { ClassDescriptor descriptor = (ClassDescriptor)descriptorEnum.nextElement(); AbstractSession session = getSessionForClass(descriptor.getJavaClass()); if (descriptor.requiresInitialization()) { descriptor.initialize(session); } } catch (RuntimeException exception) { getIntegrityChecker().handleError(exception); } } // Third initialize child dependencies for (Enumeration descriptorEnum = descriptors.elements(); descriptorEnum.hasMoreElements();) { try { ClassDescriptor descriptor = (ClassDescriptor)descriptorEnum.nextElement(); AbstractSession session = getSessionForClass(descriptor.getJavaClass()); if (descriptor.requiresInitialization()) { descriptor.postInitialize(session); } } catch (RuntimeException exception) { getIntegrityChecker().handleError(exception); } } try { getCommitManager().initializeCommitOrder(); } catch (RuntimeException exception) { getIntegrityChecker().handleError(exception); } if (getIntegrityChecker().hasErrors()) { //CR#4011 handleException(new IntegrityException(getIntegrityChecker())); } } finally { clearIntegrityChecker(); } } /** * INTERNAL: * Return if this session is a database session. */ public boolean isDatabaseSession() { return true; } /** * 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. * Durring connection attempt to auto detect the required database platform. * This method can be used in systems where for ease of use developers have * TopLink 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(); Connection conn = null; try{ conn = (Connection)getReadLogin().connectToDatasource(null); getLogin().setPlatformClassName(DBPlatformHelper.getDBPlatform(conn.getMetaData().getDatabaseProductName(), getSessionLog())); }catch (SQLException ex){ DatabaseException dbEx = DatabaseException.errorRetrieveDbMetadataThroughJDBCConnection(); // Typically exception would occur if user did not provide correct connection // parameters. The root cause of exception should be propogated up dbEx.initCause(ex); throw dbEx; }finally{ if (conn != null){ try{ conn.close(); }catch (SQLException ex){ DatabaseException dbEx = DatabaseException.errorRetrieveDbMetadataThroughJDBCConnection(); // Typically exception would occur if user did not provide correct connection // parameters. The root cause of exception should be propogated up dbEx.initCause(ex); throw dbEx; } } } 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) */ public void login() throws DatabaseException { preConnectDatasource(); connect(); postConnectDatasource(); } /** * 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. */ 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. */ public void login(Login login) throws DatabaseException { setLogin(login); login(); } /** * PUBLIC: * Disconnect from the database. * * @exception TopLinkException 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. */ public void logout() throws DatabaseException { // Reset cached data, as may be invalid later on. this.lastDescriptorAccessed = null; if (isInTransaction()) { throw DatabaseException.logoutWhileTransactionInProgress(); } if (getAccessor() == null) { return; } disconnect(); getIdentityMapAccessor().initializeIdentityMaps(); isLoggedIn = false; log(SessionLog.INFO, null, "logout_successful", this.getName()); //unregister the MBean getServerPlatform().unregisterMBean(); } /** * 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; } /** * 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())); // Login and initialize getEventManager().preLogin(this); //setup the external transaction controller getServerPlatform().initializeExternalTransactionController(); log(SessionLog.INFO, null, "topLink_version", DatasourceLogin.getVersion()); if (getServerPlatform().getServerNameAndVersion() != null) { log(SessionLog.INFO, null, "application_server_name_and_version", getServerPlatform().getServerNameAndVersion()); } } /** * INTERNAL: * This method includes all of the code that is issued after the datasource * is connected to. */ protected void postConnectDatasource(){ initializeDescriptors(); //added to process ejbQL query strings processEJBQLQueries(); log(SessionLog.INFO, null, "login_successful", this.getName()); getEventManager().postLogin(this); initializeConnectedTime(); this.isLoggedIn = true; this.platform = null; //register the MBean getServerPlatform().registerMBean(); } /** * PUBLIC: * Write all of the objects and all of their privately owned parts in the database. * The objects will be commited through a single transaction. * * @exception DatabaseException if an error occurs on the database, * these include constraint violations, security violations and general database erros. * @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(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 commited through a single transaction. * * @exception DatabaseException if an error occurs on the database, * these include constraint violations, security violations and general database erros. * @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()); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy