/*****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
****************************************************************/
package org.apache.cayenne.access;
import org.apache.cayenne.DeleteDenyException;
import org.apache.cayenne.PersistenceState;
import org.apache.cayenne.access.ObjectDiff.ArcOperation;
import org.apache.cayenne.di.Inject;
import org.apache.cayenne.graph.NodeDiff;
import org.apache.cayenne.map.DeleteRule;
import org.apache.cayenne.map.ObjEntity;
import org.apache.cayenne.map.ObjRelationship;
import org.apache.cayenne.testdo.relationships_delete_rules.DeleteRuleFlatA;
import org.apache.cayenne.testdo.relationships_delete_rules.DeleteRuleFlatB;
import org.apache.cayenne.testdo.relationships_delete_rules.DeleteRuleTest1;
import org.apache.cayenne.testdo.relationships_delete_rules.DeleteRuleTest2;
import org.apache.cayenne.testdo.relationships_delete_rules.DeleteRuleTest3;
import org.apache.cayenne.unit.di.server.CayenneProjects;
import org.apache.cayenne.unit.di.server.ServerCase;
import org.apache.cayenne.unit.di.server.UseServerRuntime;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@UseServerRuntime(CayenneProjects.RELATIONSHIPS_DELETE_RULES_PROJECT)
public class DeleteRulesIT extends ServerCase {
@Inject
private DataContext context;
@Test
public void testDenyToOne() {
DeleteRuleTest1 test1 = context.newObject(DeleteRuleTest1.class);
DeleteRuleTest2 test2 = context.newObject(DeleteRuleTest2.class);
test1.setTest2(test2);
context.commitChanges();
try {
context.deleteObjects(test1);
fail("Should have thrown an exception");
} catch (Exception e) {
// GOOD!
}
context.commitChanges();
}
@Test
public void testNoActionToOne() {
DeleteRuleTest2 test2 = context.newObject(DeleteRuleTest2.class);
DeleteRuleTest3 test3 = context.newObject(DeleteRuleTest3.class);
test3.setToDeleteRuleTest2(test2);
context.commitChanges();
// must go on without exceptions...
context.deleteObjects(test3);
context.commitChanges();
}
@Test
public void testNoActionToMany() {
DeleteRuleTest2 test2 = context.newObject(DeleteRuleTest2.class);
DeleteRuleTest3 test3 = context.newObject(DeleteRuleTest3.class);
test3.setToDeleteRuleTest2(test2);
context.commitChanges();
// must go on without exceptions...
context.deleteObjects(test2);
// don't commit, since this will cause a constraint exception
}
@Test
public void testNoActionFlattened() {
// temporarily set delete rule to NOACTION...
int oldRule = changeDeleteRule(DeleteRule.NO_ACTION);
try {
DeleteRuleFlatA a = context.newObject(DeleteRuleFlatA.class);
DeleteRuleFlatB b = context.newObject(DeleteRuleFlatB.class);
a.addToFlatB(b);
context.commitChanges();
// must go on without exceptions...
context.deleteObjects(a);
// assert that join is deleted
assertJoinDeleted(a, b);
assertEquals(PersistenceState.DELETED, a.getPersistenceState());
assertEquals(PersistenceState.COMMITTED, b.getPersistenceState());
assertTrue(b.getUntitledRel().contains(a));
context.commitChanges();
} finally {
changeDeleteRule(oldRule);
}
}
@Test
public void testNoActionFlattenedNoReverse() {
// temporarily set delete rule to NOACTION...
int oldRule = changeDeleteRule(DeleteRule.NO_ACTION);
ObjRelationship reverse = unsetReverse();
try {
DeleteRuleFlatA a = context.newObject(DeleteRuleFlatA.class);
DeleteRuleFlatB b = context.newObject(DeleteRuleFlatB.class);
a.addToFlatB(b);
context.commitChanges();
// must go on without exceptions...
context.deleteObjects(a);
// assert that join is deleted
assertJoinDeleted(a, b);
assertEquals(PersistenceState.DELETED, a.getPersistenceState());
assertEquals(PersistenceState.COMMITTED, b.getPersistenceState());
context.commitChanges();
} finally {
changeDeleteRule(oldRule);
restoreReverse(reverse);
}
}
@Test
public void testCascadeFlattened() {
// temporarily set delete rule to CASCADE...
int oldRule = changeDeleteRule(DeleteRule.CASCADE);
try {
DeleteRuleFlatA a = context.newObject(DeleteRuleFlatA.class);
DeleteRuleFlatB b = context.newObject(DeleteRuleFlatB.class);
a.addToFlatB(b);
context.commitChanges();
// must go on without exceptions...
context.deleteObjects(a);
// assert that join is deleted
assertJoinDeleted(a, b);
context.commitChanges();
assertEquals(PersistenceState.TRANSIENT, a.getPersistenceState());
assertEquals(PersistenceState.TRANSIENT, b.getPersistenceState());
} finally {
changeDeleteRule(oldRule);
}
}
@Test
public void testCascadeFlattenedNoReverse() {
// temporarily set delete rule to CASCADE...
int oldRule = changeDeleteRule(DeleteRule.CASCADE);
ObjRelationship reverse = unsetReverse();
try {
DeleteRuleFlatA a = context.newObject(DeleteRuleFlatA.class);
DeleteRuleFlatB b = context.newObject(DeleteRuleFlatB.class);
a.addToFlatB(b);
context.commitChanges();
// must go on without exceptions...
context.deleteObjects(a);
// assert that join is deleted
assertJoinDeleted(a, b);
context.commitChanges();
assertEquals(PersistenceState.TRANSIENT, a.getPersistenceState());
assertEquals(PersistenceState.TRANSIENT, b.getPersistenceState());
} finally {
changeDeleteRule(oldRule);
restoreReverse(reverse);
}
}
@Test
public void testNullifyFlattened() {
// temporarily set delete rule to NULLIFY...
int oldRule = changeDeleteRule(DeleteRule.NULLIFY);
try {
DeleteRuleFlatA a = context.newObject(DeleteRuleFlatA.class);
DeleteRuleFlatB b = context.newObject(DeleteRuleFlatB.class);
a.addToFlatB(b);
context.commitChanges();
// must go on without exceptions...
context.deleteObjects(a);
// assert that join is deleted
assertJoinDeleted(a, b);
assertEquals(PersistenceState.DELETED, a.getPersistenceState());
assertEquals(PersistenceState.MODIFIED, b.getPersistenceState());
assertFalse(b.getUntitledRel().contains(a));
context.commitChanges();
} finally {
changeDeleteRule(oldRule);
}
}
@Test
public void testNullifyFlattenedNoReverse() {
// temporarily set delete rule to NULLIFY...
int oldRule = changeDeleteRule(DeleteRule.NULLIFY);
ObjRelationship reverse = unsetReverse();
try {
DeleteRuleFlatA a = context.newObject(DeleteRuleFlatA.class);
DeleteRuleFlatB b = context.newObject(DeleteRuleFlatB.class);
a.addToFlatB(b);
context.commitChanges();
// must go on without exceptions...
context.deleteObjects(a);
// assert that join is deleted
assertJoinDeleted(a, b);
assertEquals(PersistenceState.DELETED, a.getPersistenceState());
assertEquals(PersistenceState.COMMITTED, b.getPersistenceState());
context.commitChanges();
} finally {
changeDeleteRule(oldRule);
restoreReverse(reverse);
}
}
@Test
public void testDenyFlattened() {
// temporarily set delete rule to DENY...
int oldRule = changeDeleteRule(DeleteRule.DENY);
try {
DeleteRuleFlatA a = context.newObject(DeleteRuleFlatA.class);
DeleteRuleFlatB b = context.newObject(DeleteRuleFlatB.class);
a.addToFlatB(b);
context.commitChanges();
try {
context.deleteObjects(a);
fail("Must have thrown a deny exception..");
} catch (DeleteDenyException ex) {
// expected... but check further
assertJoinNotDeleted(a, b);
}
} finally {
changeDeleteRule(oldRule);
}
}
@Test
public void testDenyFlattenedNoReverse() {
// temporarily set delete rule to DENY...
int oldRule = changeDeleteRule(DeleteRule.DENY);
ObjRelationship reverse = unsetReverse();
try {
DeleteRuleFlatA a = context.newObject(DeleteRuleFlatA.class);
DeleteRuleFlatB b = context.newObject(DeleteRuleFlatB.class);
a.addToFlatB(b);
context.commitChanges();
try {
context.deleteObjects(a);
fail("Must have thrown a deny exception..");
} catch (DeleteDenyException ex) {
// expected... but check further
assertJoinNotDeleted(a, b);
}
} finally {
changeDeleteRule(oldRule);
restoreReverse(reverse);
}
}
private int changeDeleteRule(int deleteRule) {
ObjEntity entity = context.getEntityResolver().getObjEntity(DeleteRuleFlatA.class);
ObjRelationship relationship = entity.getRelationship(DeleteRuleFlatA.FLAT_B.getName());
int oldRule = relationship.getDeleteRule();
relationship.setDeleteRule(deleteRule);
return oldRule;
}
private ObjRelationship unsetReverse() {
ObjEntity entity = context.getEntityResolver().getObjEntity(DeleteRuleFlatA.class);
ObjRelationship relationship = entity.getRelationship(DeleteRuleFlatA.FLAT_B.getName());
ObjRelationship reverse = relationship.getReverseRelationship();
if (reverse != null) {
reverse.getSourceEntity().removeRelationship(reverse.getName());
context.getEntityResolver().getClassDescriptorMap().removeDescriptor("DeleteRuleFlatA");
context.getEntityResolver().getClassDescriptorMap().removeDescriptor("DeleteRuleFlatB");
}
return reverse;
}
private void restoreReverse(ObjRelationship reverse) {
ObjEntity entity = context.getEntityResolver().getObjEntity(DeleteRuleFlatA.class);
ObjRelationship relationship = entity.getRelationship(DeleteRuleFlatA.FLAT_B.getName());
relationship.getTargetEntity().addRelationship(reverse);
context.getEntityResolver().getClassDescriptorMap().removeDescriptor("DeleteRuleFlatA");
context.getEntityResolver().getClassDescriptorMap().removeDescriptor("DeleteRuleFlatB");
}
private void assertJoinDeleted(DeleteRuleFlatA a, DeleteRuleFlatB b) {
ObjectDiff changes = context.getObjectStore().changes.get(a.getObjectId());
assertNotNull(changes);
Collection<NodeDiff> diffs = new ArrayList<NodeDiff>();
changes.appendDiffs(diffs);
Iterator<?> it = diffs.iterator();
while (it.hasNext()) {
Object diff = it.next();
if (diff instanceof ArcOperation) {
ArcOperation arcDelete = (ArcOperation) diff;
if (arcDelete.getNodeId().equals(a.getObjectId())
&& arcDelete.getTargetNodeId().equals(b.getObjectId())
&& arcDelete.getArcId().equals(DeleteRuleFlatA.FLAT_B.getName()) && arcDelete.isDelete()) {
return;
}
}
}
fail("Join was not deleted for flattened relationship");
}
private void assertJoinNotDeleted(DeleteRuleFlatA a, DeleteRuleFlatB b) {
ObjectDiff changes = context.getObjectStore().changes.get(a.getObjectId());
if (changes != null) {
Collection<NodeDiff> diffs = new ArrayList<NodeDiff>();
changes.appendDiffs(diffs);
Iterator<?> it = diffs.iterator();
while (it.hasNext()) {
Object diff = it.next();
if (diff instanceof ArcOperation) {
ArcOperation arcDelete = (ArcOperation) diff;
if (arcDelete.getNodeId().equals(a.getObjectId())
&& arcDelete.getTargetNodeId().equals(b.getObjectId())
&& arcDelete.getArcId().equals(DeleteRuleFlatA.FLAT_B.getName()) && !arcDelete.isDelete()) {
fail("Join was deleted for flattened relationship");
}
}
}
}
}
}