org.eclipse.persistence.sessions.remote.RemoteSession 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, 2018 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.config.ReferenceMode;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.history.AsOfClause;
import org.eclipse.persistence.internal.descriptors.OptimisticLockingPolicy;
import org.eclipse.persistence.internal.sessions.*;
import org.eclipse.persistence.internal.sessions.remote.*;
import org.eclipse.persistence.queries.*;
import org.eclipse.persistence.internal.queries.*;
import org.eclipse.persistence.sessions.Login;
import org.eclipse.persistence.sessions.Session;
import org.eclipse.persistence.sessions.SessionProfiler;
import org.eclipse.persistence.internal.sequencing.Sequencing;
import org.eclipse.persistence.internal.sequencing.SequencingFactory;
import org.eclipse.persistence.logging.SessionLog;
/**
* Purpose: Provide transparent remote three-tiered replacation support.
* The remote session allows for complex three-tiered applications to be easily built.
* It gives the remote client the fully functionality of the TopLink api including,
*
* - Client side caching and object-identity maintainence.
*
- Complex query support
*
- Unit of work support
*
- Indirection support through remote value holders.
*
*
* This session is a primary interface which resides on the client side. Users would interact
* with session just the same way as if it was a normal session.
*/
public class RemoteSession extends DistributedSession {
protected Sequencing sequencing;
protected boolean shouldEnableDistributedIndirectionGarbageCollection = false;
public RemoteSession() {
super(0);
}
/**
* PUBLIC:
* Creates a RemoteSession.
* @param remoteConnection remote session requires a remote connection. This must be accessed remotely from the client through RMI or CORBA.
*/
public RemoteSession(RemoteConnection remoteConnection) {
super(remoteConnection);
initializeSequencing();
}
/**
* ADVANCED:
* Allow the server-side value holders to be cleaned-up when the client-side value holder finalize.
*/
public void setShouldEnableDistributedIndirectionGarbageCollection(boolean shouldEnableDistributedIndirectionGarbageCollection) {
this.shouldEnableDistributedIndirectionGarbageCollection = shouldEnableDistributedIndirectionGarbageCollection;
}
/**
* ADVANCED:
* Allow the server-side value holders to be cleaned-up when the client-side value holder finalize.
*/
public boolean shouldEnableDistributedIndirectionGarbageCollection() {
return shouldEnableDistributedIndirectionGarbageCollection;
}
/**
* INTERNAL:
* Acquires a special historical session for reading objects as of a past time.
*/
@Override
public Session acquireHistoricalSession(AsOfClause clause) throws ValidationException {
throw ValidationException.cannotAcquireHistoricalSession();
}
/**
* 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 UnitOfWorkImpl acquireUnitOfWork() {
return acquireUnitOfWork(null);
}
/**
* 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
* @param referenceMode The reference type the UOW should use internally when
* referencing Working clones. Setting this to WEAK means the UOW will use
* weak references to reference clones and if the application no longer
* references the clone the clone may be garbage collected. If the clone
* has uncommitted changes then those changes will be lost.
*/
@Override
public UnitOfWorkImpl acquireUnitOfWork(ReferenceMode referenceMode) {
log(SessionLog.FINER, SessionLog.TRANSACTION, "acquire_unit_of_work");
setNumberOfActiveUnitsOfWork(getNumberOfActiveUnitsOfWork() + 1);
return new RemoteUnitOfWork(this, referenceMode);
}
/**
* PUBLIC:
* Return a repeatable write unit of work for this session.
* A repeatable write unit of work allows multiple writeChanges (flushes).
*
* @see RepeatableWriteUnitOfWork
*/
@Override
public RepeatableWriteUnitOfWork acquireRepeatableWriteUnitOfWork(ReferenceMode referenceMode) {
return new RemoteUnitOfWork(this, referenceMode);
}
/**
* PUBLIC:
* Execute the database query.
*/
@Override
public Object executeQuery(DatabaseQuery query) {
return query.remoteExecute(this);
}
/**
* PUBLIC:
* Return the login.
* This must retrieve the login information from the server this first time called.
* This is useful to be able to do things differently depending on the database platform.
*/
@Override
public Login getDatasourceLogin() {
Login login = super.getDatasourceLogin();
if ((login == null) && (getRemoteConnection() != null)) {
startOperationProfile(SessionProfiler.RemoteMetadata, null, SessionProfiler.ALL);
login = getRemoteConnection().getLogin();
endOperationProfile(SessionProfiler.RemoteMetadata, null, SessionProfiler.ALL);
setDatasourceLogin(login);
}
return login;
}
/**
* INTERNAL:
* Return the corresponding objects from the remote session for the objects read from the server.
*/
@Override
public Object getObjectCorrespondingTo(Object serverSideDomainObject, Map objectDescriptors, Map processedObjects, ObjectLevelReadQuery query) {
if (serverSideDomainObject == null) {
return null;
}
ClassDescriptor descriptor = getDescriptor(serverSideDomainObject);
// CR... fix to descriptor iterator exposed the bug that we were putting aggregate-collections in the cache.
if (descriptor.isAggregateCollectionDescriptor() || ((query != null) && (!query.shouldMaintainCache()))) {
if ((query != null) && (!query.hasPartialAttributeExpressions())) {
descriptor.getObjectBuilder().fixObjectReferences(serverSideDomainObject, objectDescriptors, processedObjects, query, this);
}
return serverSideDomainObject;
}
// Extract the object primary key and check if it exist on the remote session or not. If we find an object
// with this primary key then that's the corresponding object. Other wise its a new object for the remote
// session which needs to be registered in the remote sessions identity map and this is also a corresponding
// object.
ObjectDescriptor objectDescriptor = (ObjectDescriptor)objectDescriptors.get(serverSideDomainObject);
if (objectDescriptor == null){
//the object must have been added concurrently before serialize generate a new ObjectDescriptor on this side
objectDescriptor = new ObjectDescriptor();
objectDescriptor.setKey(descriptor.getObjectBuilder().extractPrimaryKeyFromObject(serverSideDomainObject, this));
objectDescriptor.setObject(serverSideDomainObject);
OptimisticLockingPolicy policy = descriptor.getOptimisticLockingPolicy();
if (policy == null){
objectDescriptor.setWriteLockValue(null);
}else{
objectDescriptor.setWriteLockValue(policy.getBaseValue());
}
objectDescriptors.put(serverSideDomainObject, objectDescriptor);
}
Object primaryKey = objectDescriptor.getKey();
Object clientSideDomainObject = getIdentityMapAccessorInstance().getFromIdentityMap(primaryKey, serverSideDomainObject.getClass(), descriptor);
// If object is already processed the return back, this check must be done after the cliet-side object is found.
if (processedObjects.containsKey(serverSideDomainObject)) {
if (clientSideDomainObject == null) {
return serverSideDomainObject;
} else {
return clientSideDomainObject;
}
}
processedObjects.put(serverSideDomainObject, serverSideDomainObject);
if (clientSideDomainObject == null) {
getIdentityMapAccessorInstance().putInIdentityMap(serverSideDomainObject, primaryKey, objectDescriptor.getWriteLockValue(), objectDescriptor.getReadTime(), descriptor);
descriptor.getObjectBuilder().fixObjectReferences(serverSideDomainObject, objectDescriptors, processedObjects, query, this);
clientSideDomainObject = serverSideDomainObject;
} else {
// if the query is null, that means we refreshed a newly-created client object at some point
// and we should refresh the identity map and cascade private parts
if ((query == null) || (query.shouldRefreshRemoteIdentityMapResult()) || getDescriptor(clientSideDomainObject).shouldAlwaysRefreshCacheOnRemote()) {
MergeManager mergeManager = new MergeManager(this);
mergeManager.refreshRemoteObject();
mergeManager.setObjectDescriptors(objectDescriptors);
if (query == null) {
mergeManager.cascadePrivateParts();
} else {
mergeManager.setCascadePolicy(query.getCascadePolicy());
}
clientSideDomainObject = mergeManager.mergeChanges(serverSideDomainObject, null, this);
}
}
return clientSideDomainObject;
}
/**
* INTERNAL:
* Return the corresponding objects from the remote session for the objects read from the server.
*/
@Override
public Object getObjectsCorrespondingToAll(Object serverSideDomainObjects, Map objectDescriptors, Map processedObjects, ObjectLevelReadQuery query, ContainerPolicy containerPolicy) {
Object clientSideDomainObjects = containerPolicy.containerInstance(containerPolicy.sizeFor(serverSideDomainObjects));
for (Object iter = containerPolicy.iteratorFor(serverSideDomainObjects);
containerPolicy.hasNext(iter);) {
Object serverSideDomainObject = containerPolicy.next(iter, this);
containerPolicy.addInto(getObjectCorrespondingTo(serverSideDomainObject, objectDescriptors, processedObjects, query), clientSideDomainObjects, this);
}
return clientSideDomainObjects;
}
/**
* INTERNAL:
* This will instantiate value holder on the server.
*/
@Override
public Object instantiateRemoteValueHolderOnServer(RemoteValueHolder remoteValueHolder) {
startOperationProfile(SessionProfiler.RemoteLazy, null, SessionProfiler.ALL);
Transporter transporter = getRemoteConnection().instantiateRemoteValueHolderOnServer(remoteValueHolder);
endOperationProfile(SessionProfiler.RemoteLazy, null, SessionProfiler.ALL);
return remoteValueHolder.getMapping().getObjectCorrespondingTo(transporter.getObject(), this, transporter.getObjectDescriptors(), new IdentityHashMap(), remoteValueHolder.getQuery());
}
/**
* INTERNAL:
* Return if this session is remote.
*/
@Override
public boolean isRemoteSession() {
return true;
}
/**
* INTERNAL:
* Return the Sequencing object used by the session.
* Sequences may be provided locally, or remotely.
*/
@Override
public Sequencing getSequencing() {
if (this.isMetadataRemote) {
return this.sequencing;
} else {
return super.getSequencing();
}
}
/**
* ADVANCED:
* Creates sequencing object for the session.
* Sequences may be provided locally, or remotely.
*/
@Override
public void initializeSequencing() {
if (this.isMetadataRemote) {
this.sequencing = SequencingFactory.createSequencing(this);
} else {
super.initializeSequencing();
}
}
}