blob: d1be9e51960231fe4b47aa7884b869d3685588ca [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.indirection;
import java.io.Serializable;
import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.indirection.ValueHolderInterface;
import org.eclipse.persistence.indirection.WeavedAttributeValueHolderInterface;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.localization.ToStringLocalization;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl;
/**
* DatabaseValueHolder wraps a database-stored object and implements
* behavior to access it. The object is read only once from database
* after which is cached for faster access.
*
* @see ValueHolderInterface
* @author Dorin Sandu
*/
public abstract class DatabaseValueHolder<T> implements WeavedAttributeValueHolderInterface<T>, Cloneable, Serializable {
/** Stores the object after it is read from the database. */
protected volatile T value;
/** Indicates whether the object has been read from the database or not. */
protected volatile boolean isInstantiated;
/** Stores the session for the database that contains the object. */
protected transient AbstractSession session;
/** Stores the row representation of the object. */
// Cannot be transient as may be required to extract the pk from a serialized object.
protected AbstractRecord row;
/**
* The variable below is used as part of the implementation of WeavedAttributeValueHolderInterface
* It is used to track whether a valueholder that has been weaved into a class is coordinated
* with the underlying property
* Set internally in EclipseLink when the state of coordination between a weaved valueholder and the underlying property is known
*/
protected boolean isCoordinatedWithProperty = false;
/**
* Default constructor.
*/
protected DatabaseValueHolder() {
}
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException exception) {
throw new InternalError();
}
}
/**
* Return the row.
*/
public AbstractRecord getRow() {
return row;
}
/**
* Return the session.
*/
public AbstractSession getSession() {
return session;
}
public ValueHolderInterface<?> getWrappedValueHolder() {
return null;
}
/**
* Return the object.
*/
@Override
public T getValue() {
boolean instantiated = this.isInstantiated;
if (!instantiated) {
synchronized (this) {
instantiated = this.isInstantiated;
if (!instantiated) {
// The value must be set directly because the setValue can also cause instantiation under UOW.
privilegedSetValue(instantiate());
this.isInstantiated = true;
postInstantiate();
resetFields();
}
}
}
return value;
}
/**
* Process against the UOW and attempt to load a local copy before going to the shared cache
* If null is returned then the calling UOW will instantiate as normal.
*/
public T getValue(UnitOfWorkImpl uow) {
//This method simply returns null as this will cause the UOWVH to trigger
//the relationship normally.
return null;
}
/**
* Instantiate the object.
*/
protected abstract T instantiate() throws DatabaseException;
/**
* 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: Implementations of this method are not necessarily thread-safe. They must
* be used in a synchronized manner
*/
public abstract T instantiateForUnitOfWorkValueHolder(UnitOfWorkValueHolder<T> unitOfWorkValueHolder);
/**
* This method is used as part of the implementation of WeavedAttributeValueHolderInterface
* It is used to check whether a valueholder that has been weaved into a class is coordinated
* with the underlying property
*/
@Override
public boolean isCoordinatedWithProperty(){
return isCoordinatedWithProperty;
}
/**
* This method is used as part of the implementation of WeavedAttributeValueHolderInterface.
*
* A DatabaseValueHolder is set up by TopLink and will never be a newly weaved valueholder.
* As a result, this method is stubbed out.
*/
@Override
public boolean isNewlyWeavedValueHolder(){
return false;
}
/**
* INTERNAL:
* Answers if this valueholder is easy to instantiate.
* @return true if getValue() won't trigger a database read.
*/
public boolean isEasilyInstantiated() {
return this.isInstantiated;
}
/**
* Return a boolean indicating whether the object
* has been read from the database or not.
*/
@Override
public boolean isInstantiated() {
return isInstantiated;
}
/**
* 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 instantiated
* all fields can not be reset.
* Note: Implementations of this method are not necessarily thread-safe. They must
* be used in a synchronizaed manner
*/
public abstract boolean isPessimisticLockingValueHolder();
/**
* Answers if this valueholder is referenced only by a UnitOfWork valueholder.
* I.e. it was built in valueFromRow which was called by buildCloneFromRow.
* <p>
* Sometimes in transaction a UnitOfWork clone, and all valueholders, are built
* directly from the row; however a UnitOfWorkValueHolder does not know how to
* instantiate itself so wraps this which does.
* <p>
* On a successful merge must be released to the session cache with
* releaseWrappedValueHolder.
*/
protected boolean isTransactionalValueHolder() {
return ((session != null) && session.isUnitOfWork());
}
/**
* Used to determine if this is a remote uow value holder that was serialized to the server.
* It has no reference to its wrapper value holder, so must find its original object to be able to instantiate.
*/
public boolean isSerializedRemoteUnitOfWorkValueHolder() {
return false;
}
/**
* INTERNAL:
* Run any extra code required after the valueholder instantiates
* @see QueryBasedValueHolder
*/
public void postInstantiate(){
//noop
}
/**
* Set the object. This is used only by the privileged methods. One must be very careful in using this method.
*/
public void privilegedSetValue(T value) {
this.value = value;
isCoordinatedWithProperty = false;
}
/**
* Releases a wrapped valueholder privately owned by a particular unit of work.
* <p>
* When unit of work clones are built directly from rows no object in the shared
* cache points to this valueholder, so it can store the unit of work as its
* session. However once that UnitOfWork commits and the valueholder is merged
* into the shared cache, the session needs to be reset to the root session, ie.
* the server session.
*/
public void releaseWrappedValueHolder(AbstractSession targetSession) {
AbstractSession session = this.session;
if ((session != null) && session.isUnitOfWork()) {
this.session = targetSession;
}
}
/**
* Reset all the fields that are not needed after instantiation.
*/
protected void resetFields() {
this.row = null;
this.session = null;
}
/**
* This method is used as part of the implementation of WeavedAttributeValueHolderInterface
* It is used internally by EclipseLink to set whether a valueholder that has been weaved into a class is coordinated
* with the underlying property
*/
@Override
public void setIsCoordinatedWithProperty(boolean coordinated){
this.isCoordinatedWithProperty = coordinated;
}
/**
* This method is used as part of the implementation of WeavedAttributeValueHolderInterface
*
* A DatabaseValueHolder is set up by EclipseLink and will never be a newly weaved valueholder
* As a result, this method is stubbed out.
*/
@Override
public void setIsNewlyWeavedValueHolder(boolean isNew){
}
/**
* Set the instantiated flag to true.
*/
public void setInstantiated() {
isInstantiated = true;
}
/**
* Set the row.
*/
public void setRow(AbstractRecord row) {
this.row = row;
}
/**
* Set the session.
*/
public void setSession(AbstractSession session) {
this.session = session;
}
/**
* Set the instantiated flag to false.
*/
public void setUninstantiated() {
isInstantiated = false;
}
/**
* Set the object.
*/
@Override
public void setValue(T value) {
this.value = value;
setInstantiated();
}
/**
* INTERNAL:
* Return if add/remove should trigger instantiation or avoid.
* Current instantiation is avoided is using change tracking.
*/
@Override
public boolean shouldAllowInstantiationDeferral() {
return true;
}
@Override
public String toString() {
if (isInstantiated()) {
return "{" + getValue() + "}";
} else {
return "{" + Helper.getShortClassName(getClass()) + ": " + ToStringLocalization.buildMessage("not_instantiated", null) + "}";
}
}
}