| /* |
| * 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); |
| } |
| } |