blob: d86f757b9604bc172440069e996c5490d21c577c [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.internal.sessions.remote;
import java.rmi.server.ObjID;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Vector;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.indirection.ValueHolderInterface;
import org.eclipse.persistence.internal.descriptors.DescriptorIterator;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.identitymaps.CacheKey;
import org.eclipse.persistence.internal.queries.CollectionContainerPolicy;
import org.eclipse.persistence.internal.queries.ContainerPolicy;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl;
import org.eclipse.persistence.queries.Cursor;
import org.eclipse.persistence.queries.CursorPolicy;
import org.eclipse.persistence.queries.CursoredStream;
import org.eclipse.persistence.queries.DatabaseQuery;
import org.eclipse.persistence.queries.ScrollableCursor;
import org.eclipse.persistence.sessions.coordination.CommandManager;
/**
* RemoteSessionController sits between the remote session (on the client)
* and whatever session is running on the server. Any interaction between these
* two classes takes place through this object.
*/
public class RemoteSessionController {
/** Used to resolve transaction treading for client-side nested transaction where server uses many worker threads. */
protected boolean isInTransaction;
/** Used to isolate queries to a unit of work in an early transaction. */
protected boolean isInEarlyTransaction;
/** This is a either a database session or a client session depending upon the setup. */
protected AbstractSession session;
/** Use the temporary unit of work to isolate queries after an early transaction. */
protected UnitOfWorkImpl unitOfWork;
/** The original remote value holders, before they were serialized; keyed by ID */
protected Map<ObjID, ValueHolderInterface> remoteValueHolders;
/** The original cursor (either CursoredStream or ScrollableCursor, before they were serialized; keyed by ID */
protected Map<ObjID, Cursor> remoteCursors;
/** This is the Synchronization policy used to synchronize remote caches */
protected CommandManager commandManager;
public RemoteSessionController(AbstractSession session) {
super();
this.commandManager = session.getCommandManager();
this.initialize(session);
}
/**
* INTERNAL:
* This method is intended to be used by by sessions that wish to execute a command on a
* remote session
* @param remoteCommand RemoteCommand The command to be executed on the remote session
*/
public Transporter processCommand(Transporter remoteCommand) {
Transporter transporter = new Transporter();
try {
((RemoteCommand)remoteCommand.getObject()).execute(getSession(), this);
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Begin a transaction on the database.
*/
public Transporter beginTransaction() {
// Must force concurrency mgrs active thread if in nested transaction.
if (isInTransaction()) {
getSession().getTransactionMutex().setActiveThread(Thread.currentThread());
}
Transporter transporter = new Transporter();
try {
getSession().beginTransaction();
// Must force concurrency mgrs active thread if in nested transaction.
if (isInTransaction()) {
getSession().getTransactionMutex().setActiveThread(Thread.currentThread());
}
if (getSession().isInTransaction()) {
setIsInTransaction(true);
}
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Begin an early unit of work transaction.
*/
public Transporter beginEarlyTransaction() {
// Must force concurrency mgrs active thread if in nested transaction.
if (isInTransaction()) {
getSession().getTransactionMutex().setActiveThread(Thread.currentThread());
}
Transporter transporter = new Transporter();
try {
getSession().beginTransaction();
// Must force concurrency mgrs active thread if in nested transaction.
if (isInTransaction()) {
getSession().getTransactionMutex().setActiveThread(Thread.currentThread());
}
if (getSession().isInTransaction()) {
this.isInTransaction = true;
this.isInEarlyTransaction = true;
}
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* build and return an object descriptor to be sent to the client
*/
protected ObjectDescriptor buildObjectDescriptor(Object object) {
ObjectDescriptor objectDescriptor = new ObjectDescriptor();
ClassDescriptor descriptor = getSession().getDescriptor(object);
Object key = descriptor.getObjectBuilder().extractPrimaryKeyFromObject(object, getSession());
objectDescriptor.setKey(key);
objectDescriptor.setWriteLockValue(getExecutionSession().getIdentityMapAccessorInstance().getWriteLockValue(key, object.getClass(), descriptor));
objectDescriptor.setObject(object);
CacheKey cacheKey = getExecutionSession().getIdentityMapAccessorInstance().getCacheKeyForObjectForLock(key, object.getClass(), descriptor);
// Check for null because when there is NoIdentityMap, CacheKey will be null
if (cacheKey != null) {
objectDescriptor.setReadTime(cacheKey.getReadTime());
}
return objectDescriptor;
}
/**
* Used for closing scrolable cursor across RMI.
*/
public Transporter closeScrollableCursor(ObjID id) {
Transporter transporter = new Transporter();
try {
ScrollableCursor stream = (ScrollableCursor)getRemoteCursors().get(id);
if (stream != null) {
stream.close();
}
getRemoteCursors().remove(id);
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Remote unit of work after serialization is commited locally.
*/
public Transporter commitRootUnitOfWork(Transporter remoteTransporter) {
remoteTransporter.expand(this.session);
// Must force concurrency mgrs active thread if in nested transaction.
if (isInTransaction()) {
getSession().getTransactionMutex().setActiveThread(Thread.currentThread());
}
RemoteUnitOfWork remoteUnitOfWork = (RemoteUnitOfWork)remoteTransporter.getObject();
// The parent is changed to the session on the server side.
remoteUnitOfWork.reinitializeForSession(getSession(), this);
Transporter transporter = new Transporter();
try {
// Committing locally.
if (remoteUnitOfWork.isFlush()) {
remoteUnitOfWork.writeChanges();
} else {
remoteUnitOfWork.commitRootUnitOfWork();
this.isInTransaction = false;
this.isInEarlyTransaction = false;
}
transporter.setObject(remoteUnitOfWork);
} catch (RuntimeException exception) {
transporter.setException(exception);
}
transporter.prepare(remoteUnitOfWork);
return transporter;
}
/**
* Commit a transaction on the database.
*/
public Transporter commitTransaction() {
Transporter transporter = new Transporter();
try {
getSession().commitTransaction();
if (!getSession().isInTransaction()) {
this.isInTransaction = false;
this.isInEarlyTransaction = false;
}
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Used for closing cursored streams across RMI.
*/
public Transporter cursoredStreamClose(Transporter remoteCursoredStreamOid) {
Transporter transporter = new Transporter();
try {
ObjID id = (ObjID) (remoteCursoredStreamOid.getObject());
CursoredStream stream = (CursoredStream) getRemoteCursors().get(id);
if (stream != null) {
stream.close();
}
getRemoteCursors().remove(id);
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Retrieve next page of objects from the remote cursored stream once, avoid page size round trips from the server to the client
*/
public Transporter cursoredStreamNextpage(Transporter remoteCursoredId, int pageSize) {
Transporter transporter = new Transporter();
Vector nextPagesObjects = null;
Map objectDescriptors = new IdentityHashMap(pageSize);
try {
CursoredStream stream = (CursoredStream)getRemoteCursors().get(remoteCursoredId.getObject());
if (stream != null) {
//retrieve page size of objects from the cursored stream
nextPagesObjects = stream.nextElements(pageSize);
for (Enumeration enumtr = nextPagesObjects.elements(); enumtr.hasMoreElements();) {
Object objectNext = enumtr.nextElement();
if (objectNext == null) {
break;
} else {
if (stream.getQuery().isReadAllQuery() && (!stream.getQuery().isReportQuery())) {
replaceValueHoldersIn(objectNext, objectDescriptors);
}
}
}
} else {
nextPagesObjects = new Vector(0);
}
} catch (RuntimeException exception) {
transporter.setException(exception);
}
transporter.setObjectDescriptors(objectDescriptors);
transporter.setObject(nextPagesObjects);
return transporter;
}
/**
* Return the wrapped cursored stream from the server
*/
public Transporter cursoredStreamSize(Transporter remoteCursoredStreamOid) {
Transporter transporter = new Transporter();
try {
//unwrap the remote cursored stream
CursoredStream stream = (CursoredStream)getRemoteCursors().get(remoteCursoredStreamOid.getObject());
if (stream != null) {
transporter.setObject(stream.size());
} else {
transporter.setObject(0);
}
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Returns a remote cursor stub in a transporter
*/
public Transporter cursorSelectObjects(Transporter remoteTransporter) {
Transporter transporter = new Transporter();
try {
CursorPolicy policy = (CursorPolicy)remoteTransporter.getObject();
// Clear the unit of work, as the client unit of work may have been cleared.
this.unitOfWork = null;
AbstractSession executionSession = getExecutionSession();
if (policy.isCursoredStreamPolicy()) {
//wrap the cursored stream into a RemoteCursoredStream object and send the object to the client
CursoredStream stream = (CursoredStream)executionSession.executeQuery(policy.getQuery());
RemoteCursoredStream remoteStream = new RemoteCursoredStream(stream);
// For bug 3452418 prevents reading the initial objects twice.
remoteStream.setObjectCollection(stream.nextElements(stream.getObjectCollection().size()));
getRemoteCursors().put(remoteStream.getID(), stream);
if (stream.getQuery().isReadAllQuery() && (!stream.getQuery().isReportQuery())) {
transporter.setObjectDescriptors(replaceValueHoldersInAll(remoteStream.getObjectCollection(), new CollectionContainerPolicy(ClassConstants.Vector_class)));
}
transporter.setObject(remoteStream);
} else if (policy.isScrollableCursorPolicy()) {
//wrap the scrollable cursor into a RemoteScrollableCursor object and send the object to the client
ScrollableCursor stream = (ScrollableCursor)executionSession.executeQuery(policy.getQuery());
RemoteScrollableCursor remoteStream = new RemoteScrollableCursor(stream);
getRemoteCursors().put(remoteStream.getID(), stream);
transporter.setObject(remoteStream);
}
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* A named query after serialization is executed locally.
*/
public Transporter executeNamedQuery(Transporter nameTransporter, Transporter classTransporter, Transporter argumentsTransporter) {
Transporter transporter = new Transporter();
try {
Object result;
DatabaseQuery query;
// Clear the unit of work, as the client unit of work may have been cleared.
this.unitOfWork = null;
AbstractSession executionSession = getExecutionSession();
if (classTransporter.getObject() == null) {
result = executionSession.executeQuery((String)nameTransporter.getObject(), (Vector)argumentsTransporter.getObject());
query = executionSession.getQuery((String)nameTransporter.getObject());
} else {
result = executionSession.executeQuery((String)nameTransporter.getObject(), (Class)classTransporter.getObject(), (Vector)argumentsTransporter.getObject());
query = executionSession.getDescriptor((Class)classTransporter.getObject()).getQueryManager().getQuery((String)nameTransporter.getObject());
}
transporter.setQuery(query);
transporter.setObjectDescriptors(query.replaceValueHoldersIn(result, this));
transporter.setObject(result);
} catch (RuntimeException exception) {
transporter.setException(exception);
}
transporter.prepare(this.session);
return transporter;
}
/**
* A remote query after serialization is executed locally.
*/
public Transporter executeQuery(Transporter remoteTransporter) {
remoteTransporter.expand(this.session);
DatabaseQuery query = (DatabaseQuery)remoteTransporter.getObject();
Transporter transporter = new Transporter();
try {
AbstractRecord argumentRow = query.getTranslationRow();
query.setTranslationRow(null);
Object result;
// Clear the unit of work, as the client unit of work may have been cleared.
this.unitOfWork = null;
AbstractSession executionSession = getExecutionSession();
if (argumentRow == null) {
result = executionSession.executeQuery(query);
} else {
result = executionSession.executeQuery(query, argumentRow);
}
transporter.setObjectDescriptors(query.replaceValueHoldersIn(result, this));
transporter.setObject(result);
} catch (RuntimeException exception) {
transporter.setException(exception);
}
transporter.prepare(this.session);
return transporter;
}
/**
* INTERNAL:
* This method returns the command manager policy for this remote connection
*/
public CommandManager getCommandManager() {
return this.commandManager;
}
/**
* return the read-only classes
**/
public Transporter getDefaultReadOnlyClasses() {
Transporter transporter = new Transporter();
try {
transporter.setObject(getSession().getDefaultReadOnlyClasses());
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Extract descriptor from the session
*/
public Transporter getDescriptor(Transporter remoteTransporter) {
Class<?> theClass = (Class)remoteTransporter.getObject();
Transporter transporter = new Transporter();
try {
ClassDescriptor descriptor = getSession().getDescriptor(theClass);
transporter.setObject(descriptor);
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Extract descriptor from the session
*/
public Transporter getDescriptorForAlias(Transporter remoteTransporter) {
String alias = (String)remoteTransporter.getObject();
Transporter transporter = new Transporter();
try {
ClassDescriptor descriptor = getSession().getDescriptorForAlias(alias);
transporter.setObject(descriptor);
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Get the associated session login.
*/
public Transporter getLogin() {
Transporter transporter = new Transporter();
try {
transporter.setObject(getSession().getDatasourceLogin());
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* return the pre-remoted cursors
*/
protected Map<ObjID, Cursor> getRemoteCursors() {
return remoteCursors;
}
/**
* INTERNAL:
* return the pre-serialized remote value holders
*/
public Map<ObjID, ValueHolderInterface> getRemoteValueHolders() {
return remoteValueHolders;
}
/**
* INTERNAL:
* Get the value returned by remote function call
*/
public Transporter getSequenceNumberNamed(Transporter remoteFunctionCall) {
Transporter transporter = new Transporter();
Object returnValue = null;
try {
RemoteFunctionCall functionCall = (RemoteFunctionCall)remoteFunctionCall.getObject();
returnValue = functionCall.execute(session, this);
} catch (RuntimeException exception) {
transporter.setException(exception);
}
transporter.setObject(returnValue);
return transporter;
}
/**
* Get the associated server side session.
*/
public AbstractSession getSession() {
return session;
}
/**
* Initialize this RemoteSessionController with a session. Clear the cached
* valueholders and cursors.
*/
protected void initialize(AbstractSession session) {
setSession(session);
setRemoteValueHolders(new Hashtable(3));
setRemoteCursors(new Hashtable(3));
setIsInTransaction(false);
}
/**
* Initalize all the server side identity maps.
*/
public Transporter initializeIdentityMapsOnServerSession() {
Transporter transporter = new Transporter();
try {
getSession().getIdentityMapAccessor().initializeAllIdentityMaps();
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* The corresponding original value holder is instantiated.
*/
public Transporter instantiateRemoteValueHolderOnServer(Transporter remoteTransporter) {
remoteTransporter.expand(this.session);
RemoteValueHolder clientValueHolder = (RemoteValueHolder)remoteTransporter.getObject();
RemoteValueHolder serverValueHolder = (RemoteValueHolder)getRemoteValueHolders().get(clientValueHolder.getID());
Transporter transporter = new Transporter();
try {
Object value = serverValueHolder.getMapping().getValueFromRemoteValueHolder(serverValueHolder);// force instantiation
transporter.setObjectDescriptors(serverValueHolder.getMapping().replaceValueHoldersIn(value, this));
// The following is a hack. Many apologies.
// The hashCode is called for the case where an Indirect Collection is being returned. The hashCode function
// causes the delegate for the collection to be build. The delegate needs to be returned to the client
// in order to properly deal with the list.
// We call hashCode after setObjectDescriptors() because the setObjectDescriptors() method will get rid of the
// delegate.
// We will have to revisit this because it causes the IndirectList to be instantiated even if it was not in the
// past - TGW
if (value != null) {
value.hashCode();
}
transporter.setObject(value);
} catch (RuntimeException exception) {
transporter.setException(exception);
}
transporter.prepare(this.session);
return transporter;
}
protected boolean isInTransaction() {
return isInTransaction;
}
/**
* Traverse the specified object, replacing the standard
* value holders with remote value holders.
* And build up a collection of object descriptors.
*/
public Map replaceValueHoldersIn(Object object) {
// 2612538 - the default size of Map (32) is appropriate
Map result = new IdentityHashMap();
replaceValueHoldersIn(object, result);
return result;
}
/**
* Traverse the specified object, replacing the standard
* value holders with remote value holders.
* Add the resulting object descriptors to the
* "collecting parm".
*/
public void replaceValueHoldersIn(Object object, Map objectDescriptors) {
if (object == null) {
return;
}
DescriptorIterator iterator = new ReplaceValueHoldersIterator(this);
iterator.setResult(objectDescriptors);
iterator.setSession(getExecutionSession());
iterator.setShouldIterateOnIndirectionObjects(true);// process the value holders themselves
iterator.setShouldIterateOverIndirectionObjects(false);// but don't go beyond them
iterator.startIterationOn(object);
}
/**
* Traverse the specified objects, replacing the standard
* value holders with remote value holders.
* And build up a collection of object descriptors.
*/
public Map replaceValueHoldersInAll(Object container, ContainerPolicy policy) {
// 2612538 - the default size of Map (32) is appropriate
Map result = new IdentityHashMap();
for (Object iter = policy.iteratorFor(container); policy.hasNext(iter);) {
replaceValueHoldersIn(policy.next(iter, getSession()), result);
}
return result;
}
/**
* Rollback a transaction on the database.
*/
public Transporter rollbackTransaction() {
Transporter transporter = new Transporter();
try {
getSession().rollbackTransaction();
if (!getSession().isInTransaction()) {
this.isInTransaction = false;
this.isInEarlyTransaction = false;
}
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Save the pre-serialized version of the remote value holder
* so that it can be used when the serialized version (on the client)
* is instantiated and needs to come back to the server to get
* its "value".
*/
public void saveRemoteValueHolder(RemoteValueHolder remoteValueHolder) {
getRemoteValueHolders().put(remoteValueHolder.getID(), remoteValueHolder);
}
/**
* Moves the cursor to the given row number in the result set
*/
public Transporter scrollableCursorAbsolute(Transporter remoteScrollableCursorOid, int rows) {
Transporter transporter = new Transporter();
try {
ScrollableCursor stream = (ScrollableCursor)getRemoteCursors().get(remoteScrollableCursorOid.getObject());
if (stream != null) {
transporter.setObject(stream.absolute(rows));
} else {
transporter.setObject(Boolean.FALSE);
}
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Moves the cursor to the end of the result set, just after the last row.
*/
public Transporter scrollableCursorAfterLast(Transporter remoteScrollableCursorOid) {
Transporter transporter = new Transporter();
try {
ScrollableCursor stream = (ScrollableCursor)getRemoteCursors().get(remoteScrollableCursorOid.getObject());
if (stream != null) {
stream.afterLast();
}
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Moves the cursor to the front of the result set, just before the first row
*/
public Transporter scrollableCursorBeforeFirst(Transporter remoteScrollableCursorOid) {
Transporter transporter = new Transporter();
try {
ScrollableCursor stream = (ScrollableCursor)getRemoteCursors().get(remoteScrollableCursorOid.getObject());
if (stream != null) {
stream.beforeFirst();
}
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Used for closing cursored streams across RMI.
*/
public Transporter scrollableCursorClose(Transporter remoteScrollableCursorOid) {
Transporter transporter = new Transporter();
try {
ScrollableCursor stream = (ScrollableCursor)getRemoteCursors().get(remoteScrollableCursorOid.getObject());
if (stream != null) {
stream.close();
}
getRemoteCursors().remove(remoteScrollableCursorOid.getObject());
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Retrieves the current row index number
*/
public Transporter scrollableCursorCurrentIndex(Transporter remoteScrollableCursorOid) {
Transporter transporter = new Transporter();
try {
ScrollableCursor stream = (ScrollableCursor)getRemoteCursors().get(remoteScrollableCursorOid.getObject());
if (stream != null) {
transporter.setObject(stream.currentIndex());
} else {
transporter.setObject(0);
}
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Moves the cursor to the first row in the result set
*/
public Transporter scrollableCursorFirst(Transporter remoteScrollableCursorOid) {
Transporter transporter = new Transporter();
try {
ScrollableCursor stream = (ScrollableCursor)getRemoteCursors().get(remoteScrollableCursorOid.getObject());
if (stream != null) {
transporter.setObject(stream.first());
} else {
transporter.setObject(Boolean.FALSE);
}
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Indicates whether the cursor is after the last row in the result set.
*/
public Transporter scrollableCursorIsAfterLast(Transporter remoteScrollableCursorOid) {
Transporter transporter = new Transporter();
try {
ScrollableCursor stream = (ScrollableCursor)getRemoteCursors().get(remoteScrollableCursorOid.getObject());
if (stream != null) {
transporter.setObject(stream.isAfterLast());
} else {
transporter.setObject(Boolean.FALSE);
}
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Indicates whether the cursor is before the first row in the result set.
*/
public Transporter scrollableCursorIsBeforeFirst(Transporter remoteScrollableCursorOid) {
Transporter transporter = new Transporter();
try {
ScrollableCursor stream = (ScrollableCursor)getRemoteCursors().get(remoteScrollableCursorOid.getObject());
if (stream != null) {
transporter.setObject(stream.isBeforeFirst());
} else {
transporter.setObject(Boolean.FALSE);
}
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Indicates whether the cursor is on the first row of the result set.
*/
public Transporter scrollableCursorIsFirst(Transporter remoteScrollableCursorOid) {
Transporter transporter = new Transporter();
try {
ScrollableCursor stream = (ScrollableCursor)getRemoteCursors().get(remoteScrollableCursorOid.getObject());
if (stream != null) {
transporter.setObject(stream.isFirst());
} else {
transporter.setObject(Boolean.FALSE);
}
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Indicates whether the cursor is on the last row of the result set.
*/
public Transporter scrollableCursorIsLast(Transporter remoteScrollableCursorOid) {
Transporter transporter = new Transporter();
try {
ScrollableCursor stream = (ScrollableCursor)getRemoteCursors().get(remoteScrollableCursorOid.getObject());
if (stream != null) {
transporter.setObject(stream.isLast());
} else {
transporter.setObject(Boolean.FALSE);
}
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Moves the cursor to the last row in the result set
*/
public Transporter scrollableCursorLast(Transporter remoteScrollableCursorOid) {
Transporter transporter = new Transporter();
try {
ScrollableCursor stream = (ScrollableCursor)getRemoteCursors().get(remoteScrollableCursorOid.getObject());
if (stream != null) {
transporter.setObject(stream.last());
} else {
transporter.setObject(Boolean.FALSE);
}
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Retrieve next object from the scrollable cursor
*/
public Transporter scrollableCursorNextObject(Transporter remoteScrollableCursorOid) {
Transporter transporter = new Transporter();
try {
ScrollableCursor stream = (ScrollableCursor)getRemoteCursors().get(remoteScrollableCursorOid.getObject());
if (stream != null) {
Object objectNext = null;
// For bug 2797683 read beyond end of stream exception should not be thrown here: called first by RemoteScrollableCursor.hasNext().
if (stream.hasNext()) {
objectNext = stream.next();
}
if (objectNext == null) {
// 2612538 - the default size of Map (32) is appropriate
transporter.setObjectDescriptors(new IdentityHashMap());
} else {
if (stream.getQuery().isReadAllQuery() && (!stream.getQuery().isReportQuery())) {
transporter.setObjectDescriptors(replaceValueHoldersIn(objectNext));
}
}
transporter.setObject(objectNext);
}
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Retrieve previous object from the scrollable cursor
*/
public Transporter scrollableCursorPreviousObject(Transporter remoteScrollableCursorOid) {
Transporter transporter = new Transporter();
try {
ScrollableCursor stream = (ScrollableCursor)getRemoteCursors().get(remoteScrollableCursorOid.getObject());
if (stream != null) {
Object objectPrevious = null;
// For bug 2797683 read beyond end of stream exception should not be thrown here: called first by RemoteScrollableCursor.hasNext().
if (stream.hasPrevious()) {
objectPrevious = stream.previous();
}
if (objectPrevious == null) {
// 2612538 - the default size of Map (32) is appropriate
transporter.setObjectDescriptors(new IdentityHashMap());
} else {
if (stream.getQuery().isReadAllQuery() && (!stream.getQuery().isReportQuery())) {
transporter.setObjectDescriptors(replaceValueHoldersIn(objectPrevious));
}
}
transporter.setObject(objectPrevious);
}
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Moves the cursor to the given row number in the result set
*/
public Transporter scrollableCursorRelative(Transporter remoteScrollableCursorOid, int rows) {
Transporter transporter = new Transporter();
try {
ScrollableCursor stream = (ScrollableCursor)getRemoteCursors().get(remoteScrollableCursorOid.getObject());
if (stream != null) {
transporter.setObject(stream.relative(rows));
} else {
transporter.setObject(Boolean.FALSE);
}
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
/**
* Return the scrollable cursor size from the server
*/
public Transporter scrollableCursorSize(Transporter remoteCursorOid) {
Transporter transporter = new Transporter();
try {
//unwrap the remote cursored stream
ScrollableCursor cursor = (ScrollableCursor)getRemoteCursors().get(remoteCursorOid.getObject());
if (cursor != null) {
transporter.setObject(cursor.size());
} else {
transporter.setObject(0);
}
} catch (RuntimeException exception) {
transporter.setException(exception);
}
return transporter;
}
protected void setIsInTransaction(boolean isInTransaction) {
this.isInTransaction = isInTransaction;
}
/**
* set the pre-remoted cursors
*/
protected void setRemoteCursors(Map<ObjID, Cursor> remoteCursors) {
this.remoteCursors = remoteCursors;
}
/**
* set the pre-serialized remote value holders
*/
protected void setRemoteValueHolders(Map<ObjID, ValueHolderInterface> remoteValueHolders) {
this.remoteValueHolders = remoteValueHolders;
}
/**
* Set the associated server side session.
*/
protected void setSession(AbstractSession session) {
this.session = session;
}
/**
* Return the correct session for the transaction context.
* If in an active transaction, a unit of work must be used to avoid putting uncommitted data into the cache,
* and to use the correct accessor for the queries.
*/
protected AbstractSession getExecutionSession() {
AbstractSession executionSession = this.session;
if (this.isInEarlyTransaction) {
if (this.unitOfWork == null) {
this.unitOfWork = this.session.acquireUnitOfWork();
this.unitOfWork.setWasTransactionBegunPrematurely(true);
}
executionSession = this.unitOfWork;
}
return executionSession;
}
}