// Look for an existing detached copy
DetachState detachState = (DetachState) state;
DetachState.Entry existingDetached = detachState.getDetachedCopyEntry(myPC);
PersistenceCapable detachedPC;
if (existingDetached == null)
{
// No existing detached copy - create new one
detachedPC = myPC.jdoNewInstance(this);
detachState.setDetachedCopyEntry(myPC, detachedPC);
}
else
{
// Found one - if it's sufficient for current FetchPlanState, return it immediately
detachedPC = (PersistenceCapable) existingDetached.getDetachedCopyObject();
if (existingDetached.checkCurrentState())
{
return detachedPC;
}
// Need to process the detached copy using current FetchPlanState
}
referencedPC = detachedPC;
// Check if detachable ... if so then we detach a copy, otherwise we return a transient copy
boolean detachable = myOM.getApiAdapter().isDetachable(myPC);
// make sure a detaching PC is not read by another thread while we are detaching
synchronized (referencedPC)
{
if (detachable)
{
if (NucleusLogger.PERSISTENCE.isDebugEnabled())
{
NucleusLogger.PERSISTENCE.debug(LOCALISER.msg("010010", StringUtils.toJVMIDString(myPC),
"" + state.getCurrentFetchDepth(), StringUtils.toJVMIDString(detachedPC)));
}
// Call any "pre-detach" listeners
getCallbackHandler().preDetach(myPC);
}
try
{
flags |= FLAG_DETACHING;
// Handle any field loading/unloading before the detach
if ((myOM.getFetchPlan().getDetachmentOptions() & FetchPlan.DETACH_LOAD_FIELDS) != 0)
{
// Load any unloaded fetch-plan fields
loadUnloadedFieldsInFetchPlan();
}
if (myLC == myOM.getNucleusContext().getApiAdapter().getLifeCycleState(LifeCycleState.HOLLOW) ||
myLC == myOM.getNucleusContext().getApiAdapter().getLifeCycleState(LifeCycleState.P_NONTRANS))
{
// Migrate any HOLLOW/P_NONTRANS to P_CLEAN etc
myLC = myLC.transitionReadField(this, true);
}
// Create a SM for our copy object
JDOStateManagerImpl smDetachedPC = new JDOStateManagerImpl(myOM, cmd);
smDetachedPC.initialiseForDetached(detachedPC, getExternalObjectId(myPC), getVersion(myPC));
smDetachedPC.referencedPC = myPC;
// If detached copy already existed, take note of fields previously loaded
if (existingDetached != null)
{
smDetachedPC.retrieveDetachState(smDetachedPC);
}
smDetachedPC.replaceFields(getFieldsNumbersToDetach(), new DetachFieldManager(this,
getSecondClassMutableFields(), myFP, state, true));
smDetachedPC.referencedPC = null;
if (detachable)
{
// Update the object with its detached state - not to be confused with the "state" object above
detachedPC.jdoReplaceFlags();
((Detachable)detachedPC).jdoReplaceDetachedState();
}
else
{
smDetachedPC.makeTransient(null);
}
// Remove its StateManager since now detached or transient
replaceStateManager(detachedPC, null);
}
catch (Exception e)
{
// What could possible be thrown here ?
NucleusLogger.PERSISTENCE.debug("DETACH ERROR : Error thrown while detaching " +
StringUtils.toJVMIDString(myPC) + " (id=" + myID + ")", e);
}
finally
{
flags &= ~FLAG_DETACHING;
referencedPC = null;
}
if (detachable && !myOM.getApiAdapter().isDetached(detachedPC))
{
// Sanity check on the objects detached state
throw new NucleusUserException(LOCALISER.msg("026025", detachedPC.getClass().getName(), myID));
}
if (detachable)
{
// Call any "post-detach" listeners