From 3c006b541a24ae29b4a5b0de7aebbb07dbf25889 Mon Sep 17 00:00:00 2001 From: Robert von Burg Date: Sat, 19 Oct 2013 21:28:11 +0200 Subject: [PATCH] [New] implemented a test to allow multiple operations This test makes sure that updating after adding, or any other operation in the same TX works properly. Found a problem where the PersistenceContext didn't implement hashCode() and equals() which lead to each operation being added even though a previous operation was already registered --- .../xmlpers/api/PersistenceContext.java | 39 +++++ .../xmlpers/objref/IdOfSubTypeRef.java | 46 +++++- .../eitchnet/xmlpers/objref/IdOfTypeRef.java | 40 +++++- .../ch/eitchnet/xmlpers/objref/ObjectRef.java | 10 +- .../ch/eitchnet/xmlpers/objref/RootRef.java | 25 ++++ .../eitchnet/xmlpers/objref/SubTypeRef.java | 37 +++++ .../ch/eitchnet/xmlpers/objref/TypeRef.java | 31 ++++ .../java/javanet/staxutils/Indentation.java | 3 +- .../staxutils/IndentingXMLStreamWriter.java | 2 + .../xmlpers/test/ObjectDaoResourceTest.java | 134 ++++++++++++++++++ 10 files changed, 360 insertions(+), 7 deletions(-) diff --git a/src/main/java/ch/eitchnet/xmlpers/api/PersistenceContext.java b/src/main/java/ch/eitchnet/xmlpers/api/PersistenceContext.java index 5a074832f..9655d5e35 100644 --- a/src/main/java/ch/eitchnet/xmlpers/api/PersistenceContext.java +++ b/src/main/java/ch/eitchnet/xmlpers/api/PersistenceContext.java @@ -52,4 +52,43 @@ public class PersistenceContext { public void setParserFactory(ParserFactory parserFactory) { this.parserFactory = parserFactory; } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((this.objectRef == null) ? 0 : this.objectRef.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + PersistenceContext other = (PersistenceContext) obj; + if (this.objectRef == null) { + if (other.objectRef != null) + return false; + } else if (!this.objectRef.equals(other.objectRef)) + return false; + return true; + } + + @SuppressWarnings("nls") + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("PersistenceContext [objectRef="); + builder.append(this.objectRef); + builder.append(", object="); + builder.append(this.object); + builder.append(", parserFactory="); + builder.append(this.parserFactory); + builder.append("]"); + return builder.toString(); + } } \ No newline at end of file diff --git a/src/main/java/ch/eitchnet/xmlpers/objref/IdOfSubTypeRef.java b/src/main/java/ch/eitchnet/xmlpers/objref/IdOfSubTypeRef.java index 8312a93da..7c7060e8a 100644 --- a/src/main/java/ch/eitchnet/xmlpers/objref/IdOfSubTypeRef.java +++ b/src/main/java/ch/eitchnet/xmlpers/objref/IdOfSubTypeRef.java @@ -73,8 +73,50 @@ public class IdOfSubTypeRef extends ObjectRef { @Override public PersistenceContext createPersistenceContext(PersistenceTransaction tx) { PersistenceContextFactoryDelegator ctxFactoryDelegator = tx.getRealm().getCtxFactoryDelegator(); - PersistenceContextFactory persistenceContextFactory = ctxFactoryDelegator - . getCtxFactory(this.type); + PersistenceContextFactory persistenceContextFactory = ctxFactoryDelegator. getCtxFactory(this.type); return persistenceContextFactory.createCtx(this); } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((this.id == null) ? 0 : this.id.hashCode()); + result = prime * result + ((this.realmName == null) ? 0 : this.realmName.hashCode()); + result = prime * result + ((this.subType == null) ? 0 : this.subType.hashCode()); + result = prime * result + ((this.type == null) ? 0 : this.type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + IdOfSubTypeRef other = (IdOfSubTypeRef) obj; + if (this.realmName == null) { + if (other.realmName != null) + return false; + } else if (!this.realmName.equals(other.realmName)) + return false; + if (this.id == null) { + if (other.id != null) + return false; + } else if (!this.id.equals(other.id)) + return false; + if (this.subType == null) { + if (other.subType != null) + return false; + } else if (!this.subType.equals(other.subType)) + return false; + if (this.type == null) { + if (other.type != null) + return false; + } else if (!this.type.equals(other.type)) + return false; + return true; + } } diff --git a/src/main/java/ch/eitchnet/xmlpers/objref/IdOfTypeRef.java b/src/main/java/ch/eitchnet/xmlpers/objref/IdOfTypeRef.java index a6f09dbef..dec93808e 100644 --- a/src/main/java/ch/eitchnet/xmlpers/objref/IdOfTypeRef.java +++ b/src/main/java/ch/eitchnet/xmlpers/objref/IdOfTypeRef.java @@ -67,8 +67,44 @@ public class IdOfTypeRef extends ObjectRef { @Override public PersistenceContext createPersistenceContext(PersistenceTransaction tx) { PersistenceContextFactoryDelegator ctxFactoryDelegator = tx.getRealm().getCtxFactoryDelegator(); - PersistenceContextFactory persistenceContextFactory = ctxFactoryDelegator - . getCtxFactory(this.type); + PersistenceContextFactory persistenceContextFactory = ctxFactoryDelegator. getCtxFactory(this.type); return persistenceContextFactory.createCtx(this); } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((this.id == null) ? 0 : this.id.hashCode()); + result = prime * result + ((this.realmName == null) ? 0 : this.realmName.hashCode()); + result = prime * result + ((this.type == null) ? 0 : this.type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + IdOfTypeRef other = (IdOfTypeRef) obj; + if (this.realmName == null) { + if (other.realmName != null) + return false; + } else if (!this.realmName.equals(other.realmName)) + return false; + if (this.id == null) { + if (other.id != null) + return false; + } else if (!this.id.equals(other.id)) + return false; + if (this.type == null) { + if (other.type != null) + return false; + } else if (!this.type.equals(other.type)) + return false; + return true; + } } diff --git a/src/main/java/ch/eitchnet/xmlpers/objref/ObjectRef.java b/src/main/java/ch/eitchnet/xmlpers/objref/ObjectRef.java index a97b4f681..356bdff4e 100644 --- a/src/main/java/ch/eitchnet/xmlpers/objref/ObjectRef.java +++ b/src/main/java/ch/eitchnet/xmlpers/objref/ObjectRef.java @@ -8,8 +8,8 @@ import ch.eitchnet.xmlpers.impl.PathBuilder; public abstract class ObjectRef extends LockableObject { - private String realmName; - private String name; + protected final String realmName; + protected final String name; protected ObjectRef(String realmName, String name) { this.realmName = realmName; @@ -44,4 +44,10 @@ public abstract class ObjectRef extends LockableObject { public String toString() { return getName(); } + + @Override + public abstract boolean equals(Object obj); + + @Override + public abstract int hashCode(); } diff --git a/src/main/java/ch/eitchnet/xmlpers/objref/RootRef.java b/src/main/java/ch/eitchnet/xmlpers/objref/RootRef.java index cb621a3d9..65640ab24 100644 --- a/src/main/java/ch/eitchnet/xmlpers/objref/RootRef.java +++ b/src/main/java/ch/eitchnet/xmlpers/objref/RootRef.java @@ -56,4 +56,29 @@ public class RootRef extends ObjectRef { String msg = MessageFormat.format("{0} is not a leaf and can thus not have a Persistence Context", getName()); //$NON-NLS-1$ throw new UnsupportedOperationException(msg); } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((this.realmName == null) ? 0 : this.realmName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + RootRef other = (RootRef) obj; + if (this.realmName == null) { + if (other.realmName != null) + return false; + } else if (!this.realmName.equals(other.realmName)) + return false; + return true; + } } diff --git a/src/main/java/ch/eitchnet/xmlpers/objref/SubTypeRef.java b/src/main/java/ch/eitchnet/xmlpers/objref/SubTypeRef.java index 560526b3d..fefcba6ef 100644 --- a/src/main/java/ch/eitchnet/xmlpers/objref/SubTypeRef.java +++ b/src/main/java/ch/eitchnet/xmlpers/objref/SubTypeRef.java @@ -63,4 +63,41 @@ public class SubTypeRef extends ObjectRef { String msg = MessageFormat.format("{0} is not a leaf and can thus not have a Persistence Context", getName()); //$NON-NLS-1$ throw new UnsupportedOperationException(msg); } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((this.realmName == null) ? 0 : this.realmName.hashCode()); + result = prime * result + ((this.subType == null) ? 0 : this.subType.hashCode()); + result = prime * result + ((this.type == null) ? 0 : this.type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + SubTypeRef other = (SubTypeRef) obj; + if (this.realmName == null) { + if (other.realmName != null) + return false; + } else if (!this.realmName.equals(other.realmName)) + return false; + if (this.subType == null) { + if (other.subType != null) + return false; + } else if (!this.subType.equals(other.subType)) + return false; + if (this.type == null) { + if (other.type != null) + return false; + } else if (!this.type.equals(other.type)) + return false; + return true; + } } diff --git a/src/main/java/ch/eitchnet/xmlpers/objref/TypeRef.java b/src/main/java/ch/eitchnet/xmlpers/objref/TypeRef.java index 44e2770a1..be34fff63 100644 --- a/src/main/java/ch/eitchnet/xmlpers/objref/TypeRef.java +++ b/src/main/java/ch/eitchnet/xmlpers/objref/TypeRef.java @@ -56,4 +56,35 @@ public class TypeRef extends ObjectRef { String msg = MessageFormat.format("{0} is not a leaf and can thus not have a Persistence Context", getName()); //$NON-NLS-1$ throw new UnsupportedOperationException(msg); } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((this.realmName == null) ? 0 : this.realmName.hashCode()); + result = prime * result + ((this.type == null) ? 0 : this.type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + TypeRef other = (TypeRef) obj; + if (this.realmName == null) { + if (other.realmName != null) + return false; + } else if (!this.realmName.equals(other.realmName)) + return false; + if (this.type == null) { + if (other.type != null) + return false; + } else if (!this.type.equals(other.type)) + return false; + return true; + } } diff --git a/src/main/java/javanet/staxutils/Indentation.java b/src/main/java/javanet/staxutils/Indentation.java index 4dfa7b48e..e7f554b92 100644 --- a/src/main/java/javanet/staxutils/Indentation.java +++ b/src/main/java/javanet/staxutils/Indentation.java @@ -4,10 +4,11 @@ package javanet.staxutils; * Characters that represent line breaks and indentation. These are represented * as String-valued JavaBean properties. */ +@SuppressWarnings("nls") public interface Indentation { /** Two spaces; the default indentation. */ - public static final String DEFAULT_INDENT = " "; + public static final String DEFAULT_INDENT = " "; /** * Set the characters used for one level of indentation. The default is diff --git a/src/main/java/javanet/staxutils/IndentingXMLStreamWriter.java b/src/main/java/javanet/staxutils/IndentingXMLStreamWriter.java index dd5438e24..aa9ffc3b6 100644 --- a/src/main/java/javanet/staxutils/IndentingXMLStreamWriter.java +++ b/src/main/java/javanet/staxutils/IndentingXMLStreamWriter.java @@ -32,6 +32,7 @@ package javanet.staxutils; import javanet.staxutils.helpers.StreamWriterDelegate; + import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; @@ -68,6 +69,7 @@ import javax.xml.stream.XMLStreamWriter; * * @author John Kristian */ +@SuppressWarnings("nls") public class IndentingXMLStreamWriter extends StreamWriterDelegate implements Indentation { public IndentingXMLStreamWriter(XMLStreamWriter out) { diff --git a/src/test/java/ch/eitchnet/xmlpers/test/ObjectDaoResourceTest.java b/src/test/java/ch/eitchnet/xmlpers/test/ObjectDaoResourceTest.java index 0932971f2..69b1f2edf 100644 --- a/src/test/java/ch/eitchnet/xmlpers/test/ObjectDaoResourceTest.java +++ b/src/test/java/ch/eitchnet/xmlpers/test/ObjectDaoResourceTest.java @@ -21,12 +21,14 @@ */ package ch.eitchnet.xmlpers.test; +import static ch.eitchnet.xmlpers.test.impl.TestConstants.TYPE_RES; import static ch.eitchnet.xmlpers.test.model.ModelBuilder.RES_ID; import static ch.eitchnet.xmlpers.test.model.ModelBuilder.RES_TYPE; import static ch.eitchnet.xmlpers.test.model.ModelBuilder.assertResource; import static ch.eitchnet.xmlpers.test.model.ModelBuilder.assertResourceUpdated; import static ch.eitchnet.xmlpers.test.model.ModelBuilder.createResource; import static ch.eitchnet.xmlpers.test.model.ModelBuilder.updateResource; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -36,12 +38,15 @@ import java.util.List; import java.util.Properties; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import ch.eitchnet.xmlpers.api.IoMode; import ch.eitchnet.xmlpers.api.ObjectDao; import ch.eitchnet.xmlpers.api.PersistenceConstants; import ch.eitchnet.xmlpers.api.PersistenceTransaction; +import ch.eitchnet.xmlpers.api.XmlPersistenceException; import ch.eitchnet.xmlpers.objref.IdOfSubTypeRef; import ch.eitchnet.xmlpers.objref.ObjectRef; import ch.eitchnet.xmlpers.objref.SubTypeRef; @@ -55,6 +60,9 @@ import ch.eitchnet.xmlpers.test.model.Resource; */ public class ObjectDaoResourceTest extends AbstractPersistenceTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + private static final String BASEPATH = "target/db/ObjectDaoTest/"; //$NON-NLS-1$ @BeforeClass @@ -226,4 +234,130 @@ public class ObjectDaoResourceTest extends AbstractPersistenceTest { assertNull("Expected that resource was deleted by ID, thus can not be read anymore", resource); //$NON-NLS-1$ } } + + @Test + public void shouldFailModifyNotExisting() { + setup(IoMode.SAX); + + this.thrown.expect(XmlPersistenceException.class); + this.thrown.expectMessage(containsString("Persistence unit does not exist for")); //$NON-NLS-1$ + + // update + try (PersistenceTransaction tx = this.persistenceManager.openTx()) { + + Resource resource = createResource(); + tx.getObjectDao().update(resource); + } + } + + @Test + public void shouldFailDeleteNotExisting() { + setup(IoMode.SAX); + + this.thrown.expect(XmlPersistenceException.class); + this.thrown.expectMessage(containsString("Persistence unit does not exist for")); //$NON-NLS-1$ + + // delete + try (PersistenceTransaction tx = this.persistenceManager.openTx()) { + + Resource resource = createResource(); + tx.getObjectDao().remove(resource); + } + } + + @Test + public void shouldAllowAllOperationsInSameTx() { + setup(IoMode.SAX); + + String subType = ModelBuilder.RES_TYPE; + String name = "shouldPersistById "; //$NON-NLS-1$ + + // create + try (PersistenceTransaction tx = this.persistenceManager.openTx()) { + + String id = "shouldAllowAllOperationsInSameTx_create"; //$NON-NLS-1$ + Resource resource = createResource(id, name, subType); + + tx.getObjectDao().add(resource); + } + + // create / modify + try (PersistenceTransaction tx = this.persistenceManager.openTx()) { + + String id = "shouldAllowAllOperationsInSameTx_create_modify"; //$NON-NLS-1$ + Resource resource = createResource(id, name, subType); + + tx.getObjectDao().add(resource); + tx.getObjectDao().update(resource); + } + + // create / delete + try (PersistenceTransaction tx = this.persistenceManager.openTx()) { + + String id = "shouldAllowAllOperationsInSameTx_create_delete"; //$NON-NLS-1$ + Resource resource = createResource(id, name, subType); + + tx.getObjectDao().add(resource); + tx.getObjectDao().remove(resource); + } + + // create / modify / delete + try (PersistenceTransaction tx = this.persistenceManager.openTx()) { + + String id = "shouldAllowAllOperationsInSameTx_create_modify_delete"; //$NON-NLS-1$ + Resource resource = createResource(id, name, subType); + + tx.getObjectDao().add(resource); + tx.getObjectDao().update(resource); + tx.getObjectDao().remove(resource); + } + + String id = "shouldAllowAllOperationsInSameTx_read_modify"; //$NON-NLS-1$ + + // prepare for read/modify + try (PersistenceTransaction tx = this.persistenceManager.openTx()) { + Resource resource = createResource(id, name, subType); + tx.getObjectDao().add(resource); + } + + // read / modify + try (PersistenceTransaction tx = this.persistenceManager.openTx()) { + + ObjectRef objectRef = tx.getObjectRefCache().getIdOfSubTypeRef(TYPE_RES, subType, id); + Object resource = tx.getObjectDao().queryById(objectRef); + assertNotNull(resource); + tx.getObjectDao().update(resource); + } + + // read / delete + try (PersistenceTransaction tx = this.persistenceManager.openTx()) { + + ObjectRef objectRef = tx.getObjectRefCache().getIdOfSubTypeRef(TYPE_RES, subType, id); + Object resource = tx.getObjectDao().queryById(objectRef); + assertNotNull(resource); + tx.getObjectDao().remove(resource); + } + + // make sure deleted, then recreate + try (PersistenceTransaction tx = this.persistenceManager.openTx()) { + + ObjectRef objectRef = tx.getObjectRefCache().getIdOfSubTypeRef(TYPE_RES, subType, id); + Object resource = tx.getObjectDao().queryById(objectRef); + assertNull(resource); + + // recreate + resource = createResource(id, name, subType); + tx.getObjectDao().add(resource); + } + + // read / modify / delete + try (PersistenceTransaction tx = this.persistenceManager.openTx()) { + + ObjectRef objectRef = tx.getObjectRefCache().getIdOfSubTypeRef(TYPE_RES, subType, id); + Object resource = tx.getObjectDao().queryById(objectRef); + assertNotNull(resource); + tx.getObjectDao().update(resource); + tx.getObjectDao().remove(resource); + } + } }