Possible to write a rollback SeamTest?
robjellinghaus Jul 11, 2006 4:03 AMOK, so pardon my lack of clue here. This is kind of a minor conundrum, but maybe someone knows the answer off the top of their head.
I am writing a base class for versioned objects. These versioned objects can make (dynamically) immutable copies of themselves. I call these copies "snapshots".
To ensure that my code doesn't modify the supposed-to-be-immutable snapshots, I have a @PreUpdate method in the base class which checks to see if the object is a snapshot (which is true iff its "replicatedChangeset" field is non-null). If it is, the @PreUpdate throws a SnapshotModifiedException.
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public abstract class RepObject {
...
private Changeset replicatedChangeset;
...
@PreUpdate
private void checkUpdate () {
log.debug("Calling checkUpdate on " + this + "...");
if (replicatedChangeset != null) {
throw new SnapshotModifiedException(this);
}
}
...
}SnapshotModifiedException:
@ApplicationException(rollback=true)
public class SnapshotModifiedException
extends RuntimeException
{
RepObject snapshot;
public SnapshotModifiedException(RepObject snapshot) {
this.snapshot = snapshot;
assert snapshot.getReplicatedChangeset() != null;
}
...
}This all works fine, actually.
So now, naturally enough, I want to write a SeamTest that tries to modify a snapshot object, and then fails if the modification is successful, and passes if the modification blows up as expected. Here's my attempt:
@Override
protected void updateModelValues()
{
entityManager = (EntityManager) Component.getInstance("entityManager", true);
changesetLog = (ChangesetLog) Contexts.getApplicationContext().get("changesetLog");
}
@Override
protected void invokeApplication()
{
// ... get a list of snapshot objects in the List priorBlogPosts ...
try {
BlogPost prior1 = priorBlogPosts.get(0);
prior1.setTitle("changedTitle"); // shouldn't change a snapshot!
// trigger the @PreUpdate check
entityManager.flush();
assert false; // plan to die if we get this far; should get exception from the flush
} catch (RuntimeException e) {
log.info("Got expected exception from attempted snapshot update", e);
// rethrow e... since it's marked as @ApplicationException(rollback=true), should roll back peacefully?!
throw e;
}
}Now, this obviously doesn't work, because I rethrow the exception, so the test dies. (Note that the exception is not a SnapshotModifiedException, it's an InvocationTargetException wrapping a SnapshotModifiedException. Oh well.)
But what should I do instead? If I swallow the exception, the test will still die, because it will try to flush again and fail. I think I want to do setRollbackOnly. But I don't actually know how to *do* setRollbackOnly from inside a SeamTest. The only syntax I see for it is (from the dvd example):
@Resource SessionContext ctx; ... ctx.setRollbackOnly();
But this can't be done from a SeamTest. If I just change my test to:
@Resource
SessionContext ctx;
...
} catch (RuntimeException e) {
log.info("Got expected exception from attempted snapshot update", e);
ctx.setRollbackOnly();
}Then I get a NullPointerException because SeamTests don't seem to support @Resource injection, so ctx is null. So how do I get a hold of a javax.ejb.SessionContext in my SeamTest to make it roll back peacefully?
Basically I'm between a rock and a hard place:
1) If I swallow the exception, then the test blows up when it tries to commit, because it flushes again and hits the exception again.
2) If I rethrow the exception, then the test blows up because an exception thrown out a test causes a test failure.
3) I can't do "ctx.setRollbackOnly();" in my catch clause because I don't know how to inject or otherwise obtain a javax.ejb.SessionContext in my test.
Obviously I could just punt on this whole test, but this is really important functionality in my little framework and I want a test for it :-)
Clues, anyone? There's probably some dead simple way to get the SessionContext from inside a SeamTest... I just can't imagine what it is!
Thanks!
Cheers,
Rob