blob: 5a4e26e2fac0a663fe2416e9ee98543ec730573f [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.io.*;
import org.eclipse.persistence.sessions.remote.*;
import org.eclipse.persistence.queries.*;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.internal.indirection.*;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.indirection.*;
import org.eclipse.persistence.mappings.*;
/**
* Remote value holders can be invoked locally and remotely.
* In both situations the associated indirect object is invoked.
*/
public class RemoteValueHolder<T> extends DatabaseValueHolder<T> implements Externalizable {
// This is a unique id for remote value holder.
protected ObjID id;
// Primary key row for target object.
protected Object targetObjectPrimaryKeys;
protected transient DatabaseMapping mapping;
protected transient ObjectLevelReadQuery query;
// The server side original value holder.
protected transient ValueHolderInterface<T> wrappedServerValueHolder;
// point back to the object holding the remote value holder;
// for the moment, used only by TransparentIndirection
protected transient Object serverIndirectionObject;
public RemoteValueHolder() {
// This assigns unique id to the remote value holder when it is created.
this.id = new ObjID();
}
public RemoteValueHolder(ObjID id) {
this.id = id;
}
/**
* If the reference object is mapped thru one to one mapping and the object derives its primary key value
* from this relationship then remote value holder has a primary key row for.
*/
protected boolean canDoCacheCheck() {
if ((getMapping() == null) || (getQuery() == null)) {
return false;
}
return (getTargetObjectPrimaryKeys() != null) && getMapping().isOneToOneMapping() && (!getQuery().shouldRefreshIdentityMapResult()) && (!getQuery().shouldRefreshRemoteIdentityMapResult()) && (getQuery().shouldMaintainCache());
}
/**
* Only the id is checked for equality check.
*/
@Override
public boolean equals(Object object) {
if (!(object instanceof RemoteValueHolder)) {
return false;
}
return getID().equals(((RemoteValueHolder<?>)object).getID());
}
/**
* This method is used to remove the RemoteValueHolder from the dispatcher on Garbage
* collection from the client
*/
@Override
public void finalize() {
if ((this.session != null) && (this.session instanceof RemoteSession) && ((RemoteSession)this.session).shouldEnableDistributedIndirectionGarbageCollection()) {
// This must be on the client
RemoveServerSideRemoteValueHolderCommand command = new RemoveServerSideRemoteValueHolderCommand(getID());
((DistributedSession)getSession()).getRemoteConnection().processCommand(command);
//remove this value holder from the session dispatcher as it will no longer be needed
}
}
/**
* Return the unique id.
*/
public ObjID getID() {
return id;
}
/**
* Return the associated mapping.
*/
public DatabaseMapping getMapping() {
return mapping;
}
/**
* Get object from the cache if there is one.
*/
protected Object getObjectFromCache() {
Object cachedObject = null;
if (getMapping().isOneToOneMapping()) {
ClassDescriptor descriptor = getMapping().getReferenceDescriptor();
cachedObject = getSession().getIdentityMapAccessorInstance().getFromIdentityMap(getTargetObjectPrimaryKeys(), descriptor.getJavaClass(), descriptor);
}
return cachedObject;
}
/**
* Return the associated query.
*/
public ObjectLevelReadQuery getQuery() {
return query;
}
/**
* Return the object on the server that holds on
* to the remote value holder.
* Currently used only by TransparentIndirection so we
* can get back to the original IndirectContainer.
*/
public Object getServerIndirectionObject() {
return serverIndirectionObject;
}
/**
* Get target object primary key row.
*/
protected Object getTargetObjectPrimaryKeys() {
return targetObjectPrimaryKeys;
}
/**
* Return the original value holder.
* This is null on the client side because it is tagged transient.
* This is how we know whether the remote value holder is
* being invoked on the client or on the server.
*/
public ValueHolderInterface<T> getWrappedServerValueHolder() {
return wrappedServerValueHolder;
}
/**
* Return the hashcode for id, because it is unqiue.
*/
@Override
public int hashCode() {
return getID().hashCode();
}
/**
* Return the object.
*/
@Override
@SuppressWarnings({"unchecked"})
public synchronized T instantiate() {
T valueOfServerValueHolder = null;
if (getWrappedServerValueHolder() != null) {// server invocation
valueOfServerValueHolder = getWrappedServerValueHolder().getValue();
} else {// client invocation
// check whether object exists on the client
if (canDoCacheCheck()) {
valueOfServerValueHolder = (T) getObjectFromCache();
}
// does not exist on the client - so invoke the value holder on the server
if (valueOfServerValueHolder == null) {
valueOfServerValueHolder = (T) ((DistributedSession)getSession()).instantiateRemoteValueHolderOnServer(this);
}
}
return valueOfServerValueHolder;
}
/**
* INTERNAL:
* Answers if this valueholder is easy to instantiate.
* @return true if getValue() won't trigger a database read.
*/
@Override
public boolean isEasilyInstantiated() {
// Nothing is easily instantiated when on the client side.
return this.isInstantiated || ((this.wrappedServerValueHolder != null)
&& (!(this.wrappedServerValueHolder instanceof DatabaseValueHolder) || ((DatabaseValueHolder<T>)this.wrappedServerValueHolder).isEasilyInstantiated()));
}
/**
* INTERNAL:
* Answers if this valueholder is a pessimistic locking one. Such valueholders
* are special in that they can be triggered multiple times by different
* UnitsOfWork. Each time a lock query will be issued. Hence even if
* instantiated it may have to be instantiated again, and once instantatiated
* all fields can not be reset.
* Note: This method is not thread-safe. It must be used in a synchronizaed manner
*/
@Override
public boolean isPessimisticLockingValueHolder() {
// This abstract method needs to be implemented but is not meaningfull for
// this subclass.
if (getWrappedServerValueHolder() != null) {
return ((getWrappedServerValueHolder() instanceof DatabaseValueHolder) && ((DatabaseValueHolder<T>)getWrappedServerValueHolder()).isPessimisticLockingValueHolder());
} else {
// Pessimistic locking may not be supported on remote sessions, but if
// it is make every attempt to do the right thing.
return ((getQuery() != null) && getQuery().isLockQuery(getSession()));
}
}
/**
* Triggers UnitOfWork valueholders directly without triggering the wrapped
* valueholder (this).
* <p>
* When in transaction and/or for pessimistic locking the UnitOfWorkValueHolder
* needs to be triggered directly without triggering the wrapped valueholder.
* However only the wrapped valueholder knows how to trigger the indirection,
* i.e. it may be a batchValueHolder, and it stores all the info like the row
* and the query.
* Note: This method is not thread-safe. It must be used in a synchronizaed manner
*/
@Override
public T instantiateForUnitOfWorkValueHolder(UnitOfWorkValueHolder<T> unitOfWorkValueHolder) {
if ((getWrappedServerValueHolder() != null) && (getWrappedServerValueHolder() instanceof DatabaseValueHolder)) {
DatabaseValueHolder<T> wrapped = (DatabaseValueHolder<T>)getWrappedServerValueHolder();
return wrapped.instantiateForUnitOfWorkValueHolder(unitOfWorkValueHolder);
}
// The scenario of triggering a valueholder in transaction when
// the RemoteUnitOfWork is on the client side may be impossible.
return unitOfWorkValueHolder.buildCloneFor(getValue());
}
/**
* Override the default serialization for a remote valueholder so as not to serialize the value
* Note: Changed for bug 3145211. We used to use the java.io.Serializable interface, but need to convert
* to Externalizable interface to avoid sending extra data through the superclass's serialization
*/
@Override
public void readExternal(ObjectInput in) throws IOException, java.lang.ClassNotFoundException {
this.id = (ObjID)in.readObject();
this.targetObjectPrimaryKeys = in.readObject();
this.row = (AbstractRecord)in.readObject();
this.isInstantiated = in.readBoolean();
}
/**
* Set the unique id.
*/
protected void setID(ObjID anID) {
this.id = anID;
}
/**
* Set mapping
*/
public void setMapping(DatabaseMapping mapping) {
this.mapping = mapping;
}
/**
* Set the query.
*/
public void setQuery(ObjectLevelReadQuery query) {
this.query = query;
}
/**
* Set the object on the server that holds on
* to the remote value holder.
* Currently used only by TransparentIndirection so we
* can get back to the original IndirectContainer.
*/
public void setServerIndirectionObject(Object serverIndirectionObject) {
this.serverIndirectionObject = serverIndirectionObject;
}
/**
* Set target object primary keys.
*/
public void setTargetObjectPrimaryKeys(Object primaryKeys) {
this.targetObjectPrimaryKeys = primaryKeys;
}
/**
* Set the object.
*/
@Override
public void setValue(T theValue) {
super.setValue(theValue);
if (getWrappedServerValueHolder() != null) {
// This is a local setting of remote value holder
// and will only happen with basic indirection
getWrappedServerValueHolder().setValue(theValue);
}
}
/**
* Set the original value holder.
*/
public void setWrappedServerValueHolder(ValueHolderInterface<T> wrappedServerValueHolder) {
this.wrappedServerValueHolder = wrappedServerValueHolder;
}
/**
* Override the default serialization for a remote valueholder so as not to serialize the value
* Note: Changed for bug 3145211. We used to use the java.io.Serializable interface, but need to convert
* to Externalizable interface to avoid sending extra data through the superclass's serialization
*/
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(id);
out.writeObject(targetObjectPrimaryKeys);
out.writeObject(row);
out.writeBoolean(false);
}
}