blob: 2bcdcfe30b30be59a67ad7cf642dd6f191260b49 [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. 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
package org.eclipse.persistence.sessions.remote;
import java.util.*;
import org.eclipse.persistence.internal.helper.*;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.DescriptorQueryManager;
import org.eclipse.persistence.exceptions.*;
import org.eclipse.persistence.internal.sessions.remote.*;
import org.eclipse.persistence.queries.*;
import org.eclipse.persistence.sessions.Login;
import org.eclipse.persistence.sessions.SessionProfiler;
import org.eclipse.persistence.internal.queries.*;
import org.eclipse.persistence.internal.identitymaps.*;
import org.eclipse.persistence.internal.sessions.*;
/**
* <b>Purpose</b>: Super class to all remote client session's.
*/
public abstract class DistributedSession extends DatabaseSessionImpl {
/** Connection to remote persistence service. */
protected transient RemoteConnection remoteConnection;
/** Cache if default classes have been read from server. */
protected boolean hasDefaultReadOnlyClasses;
/** Define if meta-data is initialized locally, or serialized from the server. */
protected boolean isMetadataRemote = true;
/**
* INTERNAL:
* Create a blank session, used for proxy session.
*/
protected DistributedSession(int nothing) {
}
/**
* PUBLIC:
* Creates a DistributedSession.
* @param remoteConnection remote session requires a remote connection. This must be accessed remotely from the client through RMI or CORBA.
*/
protected DistributedSession(RemoteConnection remoteConnection) {
this.remoteConnection = remoteConnection;
this.remoteConnection.initialize(this);
this.project = new org.eclipse.persistence.sessions.Project();
}
/**
* PUBLIC:
* Return a unit of work for this session.
* The unit of work is an object level transaction that allows
* a group of changes to be applied as a unit.
*
* @see UnitOfWorkImpl
*/
@Override
public abstract UnitOfWorkImpl acquireUnitOfWork();
/**
* PUBLIC:
* Start a transaction on the server.
* A unit of work should normally be used instead of transactions for the remote session.
*/
@Override
public void beginTransaction() {
// Acquire the mutex so session knows it is in a transaction.
getTransactionMutex().acquire();
startOperationProfile(SessionProfiler.Remote, null, SessionProfiler.ALL);
getRemoteConnection().beginTransaction();
endOperationProfile(SessionProfiler.Remote, null, SessionProfiler.ALL);
}
/**
* PUBLIC:
* Commit a transaction on the server.
* A unit of work should normally be used instead of transactions for the remote session.
*/
@Override
public void commitTransaction() {
startOperationProfile(SessionProfiler.Remote, null, SessionProfiler.ALL);
getRemoteConnection().commitTransaction();
endOperationProfile(SessionProfiler.Remote, null, SessionProfiler.ALL);
getTransactionMutex().release();
}
/**
* INTERNAL:
* Return remote cursor stream.
*/
public RemoteCursoredStream cursorSelectObjects(CursoredStreamPolicy policy) {
return getRemoteConnection().cursorSelectObjects(policy, this);
}
/**
* INTERNAL:
* Return remote scrollable cursor
*/
public RemoteScrollableCursor cursorSelectObjects(ScrollableCursorPolicy policy) {
return getRemoteConnection().cursorSelectObjects(policy, this);
}
/**
* PUBLIC:
* Execute the pre-defined query by name and return the result.
* Queries can be pre-defined and named to allow for their reuse.
* The named query can be defined on the remote session or the server-side session.
*
* @see #addQuery(String, DatabaseQuery)
*/
@Override
public Object executeQuery(String queryName) throws DatabaseException {
return executeQuery(queryName, new Vector(1));
}
/**
* PUBLIC:
* Execute the pre-defined query by name and return the result.
* Queries can be pre-defined and named to allow for their reuse.
* The class is the descriptor in which the query was pre-defined.
* The query is executed on the server-side session.
*
* @see DescriptorQueryManager#addQuery(String, DatabaseQuery)
*/
@Override
public Object executeQuery(String queryName, Class domainClass) throws DatabaseException {
return executeQuery(queryName, domainClass, new Vector(1));
}
/**
* PUBLIC:
* Execute the pre-defined query by name and return the result.
* Queries can be pre-defined and named to allow for their reuse.
* The class is the descriptor in which the query was pre-defined.
*
* @see DescriptorQueryManager#addQuery(String, DatabaseQuery)
*/
@Override
public Object executeQuery(String queryName, Class domainClass, Vector argumentValues) throws DatabaseException {
startOperationProfile(SessionProfiler.Remote, null, SessionProfiler.ALL);
Transporter transporter = getRemoteConnection().remoteExecuteNamedQuery(queryName, domainClass, argumentValues);
endOperationProfile(SessionProfiler.Remote, null, SessionProfiler.ALL);
transporter.getQuery().setSession(this);
return transporter.getQuery().extractRemoteResult(transporter);
}
/**
* PUBLIC:
* Execute the pre-defined query by name and return the result.
* Queries can be pre-defined and named to allow for their reuse.
*
* @see #addQuery(String, DatabaseQuery)
*/
@Override
public Object executeQuery(String queryName, Vector argumentValues) throws DatabaseException {
if (containsQuery(queryName)) {
return super.executeQuery(queryName, argumentValues);
}
return executeQuery(queryName, null, argumentValues);
}
/**
* Execute the database query.
*/
@Override
public abstract Object executeQuery(DatabaseQuery query);
/**
* INTERNAL:
* Execute the database query.
*/
@Override
public Object executeQuery(DatabaseQuery query, AbstractRecord row) {
query.setTranslationRow(row);
return executeQuery(query);
}
/**
* INTERNAL:
* CR#2751
* Returns the set of read-only classes for the receiver. These class come from the
* Remote connection
* @return A Vector containing the Java Classes that are currently read-only.
*/
@Override
public Vector getDefaultReadOnlyClasses() {
if (this.isMetadataRemote && !this.hasDefaultReadOnlyClasses) {
getProject().setDefaultReadOnlyClasses(getRemoteConnection().getDefaultReadOnlyClasses());
this.hasDefaultReadOnlyClasses = true;
}
return super.getDefaultReadOnlyClasses();
}
/**
* INTERNAL:
* Return the table descriptor specified for the class.
*/
@Override
public ClassDescriptor getDescriptor(Class domainClass) {
ClassDescriptor descriptor = getDescriptors().get(domainClass);
// If the descriptor is null then this means that descriptor must now be read from the server.
if (descriptor == null) {
if (!this.isMetadataRemote) {
return super.getDescriptor(domainClass);
}
startOperationProfile(SessionProfiler.RemoteMetadata, null, SessionProfiler.ALL);
descriptor = getRemoteConnection().getDescriptor(domainClass);
endOperationProfile(SessionProfiler.RemoteMetadata, null, SessionProfiler.ALL);
if (descriptor == null) {
return super.getDescriptor(domainClass);
}
getDescriptors().put(domainClass, descriptor);
String alias = descriptor.getAlias();
if (alias != null) {
getProject().addAlias(alias, descriptor);
}
descriptor.remoteInitialization(this);
}
return descriptor;
}
/**
* INTERNAL:
* Return the table descriptor specified for the class.
*/
@Override
public ClassDescriptor getDescriptorForAlias(String alias) {
ClassDescriptor descriptor = super.getDescriptorForAlias(alias);
// If the descriptor is null then this means that descriptor must now be read from the server.
if (descriptor == null) {
if (!this.isMetadataRemote) {
return null;
}
startOperationProfile(SessionProfiler.RemoteMetadata, null, SessionProfiler.ALL);
descriptor = getRemoteConnection().getDescriptorForAlias(alias);
endOperationProfile(SessionProfiler.RemoteMetadata, null, SessionProfiler.ALL);
if (descriptor == null) {
return null;
}
getDescriptors().put(descriptor.getJavaClass(), descriptor);
getProject().addAlias(alias, descriptor);
descriptor.remoteInitialization(this);
}
return descriptor;
}
/**
* INTERNAL:
* Return the descriptor.
*/
public ClassDescriptor getDescriptorCorrespondingTo(ClassDescriptor descriptor) {
return getDescriptors().get(descriptor.getJavaClass());
}
/**
* INTERNAL:
* Return the corresponding objects from the remote session for the objects read from the server.
*/
public abstract Object getObjectCorrespondingTo(Object serverSideDomainObject, Map objectDescriptors, Map processedObjects, ObjectLevelReadQuery query);
/**
* INTERNAL:
* Return the corresponding objects from the remote session for the objects read from the server.
*/
public abstract Object getObjectsCorrespondingToAll(Object serverSideDomainObjects, Map objectDescriptors, Map processedObjects, ObjectLevelReadQuery query, ContainerPolicy containerPolicy);
/**
* INTERNAL:
* Return the remote connection.
*/
public RemoteConnection getRemoteConnection() {
return remoteConnection;
}
/**
* INTERNAL:
* Checks if the descriptor exists or not.
*/
public boolean hasCorrespondingDescriptor(ClassDescriptor descriptor) {
return getDescriptors().containsKey(descriptor.getJavaClass());
}
/**
* INTERNAL:
* Set up the IdentityMapManager. Overrides the default IdentityMapManager
*/
@Override
public void initializeIdentityMapAccessor() {
this.identityMapAccessor = new DistributedSessionIdentityMapAccessor(this, new IdentityMapManager(this));
}
/**
* INTERNAL:
* This will instantiate value holder on the server.
*/
public abstract Object instantiateRemoteValueHolderOnServer(RemoteValueHolder remoteValueHolder);
/**
* PUBLIC:
* Return if this session is connected to the server.
*/
@Override
public boolean isConnected() {
if (getRemoteConnection() == null) {
return false;
}
return getRemoteConnection().isConnected();
}
/**
* INTERNAL:
* Return if this session is a distributed session.
*/
@Override
public boolean isDistributedSession() {
return true;
}
/**
* INTERNAL:
* Return if this session is a remote session.
*/
@Override
public boolean isRemoteSession() {
return false;
}
/**
* INTERNAL:
* You cannot add descriptors to a remote session. This is a internal method used by TopLink
*/
public void privilegedAddDescriptor(ClassDescriptor descriptor) {
getDescriptors().put(descriptor.getJavaClass(), descriptor);
}
/**
* PUBLIC:
* Rollback a transaction on the server.
* A unit of work should normally be used instead of transactions for the remote session.
*/
@Override
public void rollbackTransaction() {
startOperationProfile(SessionProfiler.Remote, null, SessionProfiler.ALL);
getRemoteConnection().rollbackTransaction();
endOperationProfile(SessionProfiler.Remote, null, SessionProfiler.ALL);
getTransactionMutex().release();
}
/**
* INTERNAL:
* Set the remote connection.
*/
public void setRemoteConnection(RemoteConnection remoteConnection) {
this.remoteConnection = remoteConnection;
}
/**
* PUBLIC:
* Avoid printing the accessor and platform.
*/
@Override
public String toString() {
return Helper.getShortClassName(getClass()) + "()";
}
/**
* PUBLIC:
* Logout the session, close the remote connection and release the hold resources
*/
@Override
public void logout() {
//CR3854: logging out of DistributedSession is not releasing remote resources.
//The remote connection remove() call should release the resource, like the stateful
//session been, which the connection holds on.
this.remoteConnection.release();
}
/**
* ADVANCED:
* Return if the descriptors and meta-data should be serialized from the server,
* or if they will be provided locally.
*/
public boolean isMetadataRemote() {
return isMetadataRemote;
}
/**
* ADVANCED:
* Set if the descriptors and meta-data should be serialized from the server,
* or if they will be provided locally.
*/
public void setIsMetadataRemote(boolean isMetadataRemote) {
this.isMetadataRemote = isMetadataRemote;
}
/**
* INTERNAL:
* Connect not required.
*/
@Override
public void connect() throws DatabaseException {
this.remoteConnection.initialize(this);
}
/**
* INTERNAL:
* Disconnect not required.
*/
@Override
public void disconnect() throws DatabaseException {
getSequencingHome().onDisconnect();
}
/**
* 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.
*/
@Override
public void loginAndDetectDatasource() throws DatabaseException {
preConnectDatasource();
connect();
Login login = this.remoteConnection.getLogin();
setLogin(login);
postConnectDatasource();
}
/**
* PUBLIC:
* Connect to the database using the predefined login.
* Obtain the login from the server, as it may have configuration initialized from the database meta-data.
*/
@Override
public void login() throws DatabaseException {
preConnectDatasource();
connect();
Login login = this.remoteConnection.getLogin();
setLogin(login);
postConnectDatasource();
}
}