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