/******************************************************************************* * Copyright (c) 1998, 2015 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 v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * 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 implements WeavedAttributeValueHolderInterface, Cloneable, Serializable { /** Stores the object after it is read from the database. */ protected volatile Object 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; @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 Object 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 Object 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 Object 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 Object instantiateForUnitOfWorkValueHolder(UnitOfWorkValueHolder 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(Object 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(Object 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", (Object[])null) + "}"; } } }