blob: 2bcec53bd58d545beaddaa2aecbc9273e4aab308 [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.testing.tests.readonly;
import java.util.*;
import org.eclipse.persistence.sessions.*;
import org.eclipse.persistence.testing.models.readonly.HollywoodAgent;
import org.eclipse.persistence.testing.models.readonly.Actor;
import org.eclipse.persistence.testing.models.readonly.ReadOnlyHollywoodAgent;
import org.eclipse.persistence.testing.models.readonly.Charity;
import org.eclipse.persistence.testing.models.readonly.ReadOnlyCharity;
import org.eclipse.persistence.testing.framework.TestErrorException;
import org.eclipse.persistence.testing.framework.TransactionalTestCase;
/**
* Test added for bug 3013948. Since one can modify oneToOne or manyToMany
* references to read only objects without changing those objects, this is
* something we should fully support.
*
* Bug 3013948 fixes the read only case of CR 1360. The original description
* is:
*
* Description: There was an issue that loss of identity could cause a problem with deletes from collection mappings.
* Consider an object model where an Employee contains a privately owned vector of PhoneNumbers. Also, assume that the IdentityMap for PhoneNumber is a CacheIdentityMap of size 10. Now, imagine that 10 Employees, each with 3 PhoneNumbers are read in. Because the identityMap is of a fixed size, only 10 of the phone number will be in the cache. Since PhoneNumbers are privately owned, deleting them out of the vector of the Employee will delete them from the database. However, in the above scenario, if the PhoneNumber is not longer in the identityMap, the delete would occur on the database, but not in the original.
*
* Also, if this collection were not a Vector but a Hashtable, there would be a NullPointer exception when the merge to the original object was attempted..
*
* Note: The above scenario is generally one that you should avoid if possible. Loss of identity may also cause other unexpected behavior. In general it is better to use an identity map that will maintain identity for the life of you domain objects.
*
* Symptom:Either a nullpointer exeption on merge, or an incorrect merge due to loss of identity.
*
*/
public class WriteableMappingReadOnlyClassTest extends TransactionalTestCase {
protected UnitOfWork uow;
protected Actor actorClone;
public WriteableMappingReadOnlyClassTest() {
super();
}
@Override
protected void setup() {
super.setup();
// These two lines are needed due to problems with ReadOnlyClassAccessingTestCase.
getSession().getDescriptor(ReadOnlyCharity.class).setShouldBeReadOnly(true);
getSession().getDescriptor(ReadOnlyHollywoodAgent.class).setShouldBeReadOnly(true);
}
@Override
protected void test() {
Vector charities = getSession().readAllObjects(Charity.class);
HollywoodAgent hollywoodAgent = (HollywoodAgent)getSession().readObject(HollywoodAgent.class);
// Now create a new Actor, and assign it read only versions of the previous.
uow = getSession().acquireUnitOfWork();
Actor actor = new Actor();
for (Enumeration enumtr = charities.elements(); enumtr.hasMoreElements();) {
Charity charity = (Charity)enumtr.nextElement();
ReadOnlyCharity readOnlyCharity = new ReadOnlyCharity();
readOnlyCharity.id = charity.id;
readOnlyCharity.setName(charity.getName());
actor.addCharity(readOnlyCharity);
}
ReadOnlyHollywoodAgent readOnlyHollywoodAgent = new ReadOnlyHollywoodAgent();
readOnlyHollywoodAgent.id = hollywoodAgent.id;
readOnlyHollywoodAgent.setName(hollywoodAgent.getName());
actor.setHollywoodAgent(readOnlyHollywoodAgent);
setActorClone((Actor)uow.registerObject(actor));
uow.commit();
// Prior to 3013948 the merge would have failed and the actor in the
// global cache would have all their read only references replaced by nulls.
}
@Override
protected void verify() {
try {
uow = getSession().acquireUnitOfWork();
Actor actorClone = getActorClone();
actorClone.getCharities().removeAllElements();
actorClone.setHollywoodAgent(null);
Actor original = (Actor)uow.readObject(actorClone);
Actor sessionOriginal = (Actor)getSession().readObject(actorClone);
actorClone = (Actor)uow.deepMergeClone(actorClone);
uow.commit();
} catch (Exception e) {
throw new TestErrorException("The original commit corrupted the session cache.", e);
}
Actor verification = (Actor)getSession().readObject(actorClone);
if ((verification.getHollywoodAgent() != null) || (verification.getCharities().size() != 0)) {
throw new TestErrorException("An exception was not thrown but the updates did not work.");
}
}
@Override
public void reset() {
super.reset();
}
protected Actor getActorClone() {
return actorClone;
}
protected void setActorClone(Actor actorClone) {
this.actorClone = actorClone;
}
}