0

I have an object that user typically creates using my factory. I want to make sure my user calls object.undo() before they throw it into the garbage collector.

This class setups the necessary resources so the user isn't writing to production services and instead use test specific services. The user needs to call undo to revert their override.

Here is what I want the user to do

// in setup()
FooObject object = FooFactory.overrideProductionServicesToTestServices();

// do your testing in test*()

// in tearDown()
object.undo()

Here is what the user end up doing

// in setup()
FooFactory.overrideProductionServicesToTestServices()

// do their testing. Everything works.

// Another test, everything breaks

How do I notify the user either via throwing an exception or via class design that the user should call undo()?

Dat Chu
  • 10,822
  • 13
  • 58
  • 82
  • An object will not be collected if there is still a reference to it, so keep the reference until `object.undo()` is called. – Scary Wombat Sep 04 '14 at 00:40
  • I have found that the user do: factory.makeObject() without putting it into a variable. This causes the object to be collected and the behavior of the services erratic. – Dat Chu Sep 04 '14 at 00:44

3 Answers3

1

Instead of using a finalizer, which carries a performance impact alongside being rather easy to get wrong, why not store a PhantomReference to the created object inside the factory?

From the Javadocs:

Phantom references are most often used for scheduling pre-mortem cleanup actions in a more flexible way than is possible with the Java finalization mechanism.

You could have the factory track the associated ReferenceQueue for that reference, and once the object is enqueued, perform the cleanup actions ordinarily done by undo.

Thorn G
  • 12,620
  • 2
  • 44
  • 56
  • Since the undo() method belongs to the referent which cannot be accessed via get(), how do I know that my referent has called undo() prior to the PhantomReference be enqueued? – Dat Chu Sep 04 '14 at 14:33
  • True -- you can't call undo() directly. However, you could still free whatever resources are being cleaned up by undo when you see the reference get enqueued. – Thorn G Sep 04 '14 at 17:24
  • Thank you for the answer. I wasn't aware of java references mechanism. – Dat Chu Sep 04 '14 at 19:14
0

I want to make sure my user calls object.undo() before they throw it into the garbage collector.

You can implement the finalize() method and call the object.undo() inside it to make sure it runs first before it's garbage collected.

Like this:

protected void finalize()
             throws Throwable {
    this.undo();
}

To read more about finalize() method, go to this article on Oracle.

lxcky
  • 1,668
  • 2
  • 13
  • 26
  • 2
    Or check that `object.undo()` has been called, rather than calling it. That way, you can test that everything calls `undo` correctly, and then you could remove it from the finalizer later (for performance reasons) without breaking things. – user253751 Sep 04 '14 at 00:49
0

finalizer can't throw exceptions

Yes it can, but you must be careful about it. The correct way to call a method in finalize() is as follows:

protected void finalize() throws Throwable
{
    try
    {
       myMethod();
    }
    finally
    {
      super.finalize();
    }
}
user207421
  • 305,947
  • 44
  • 307
  • 483
  • "Any exception thrown by the finalize method causes the finalization of this object to be halted, but is otherwise ignored." I meant to refer to the fact that throwing an exception in a finalize method does not inform the user that their code usage is wrong. – Dat Chu Sep 04 '14 at 01:25
  • Then you need to clarify your question, don't you? At present it states a falsehood, and doesn't state your requirement correctly. – user207421 Sep 04 '14 at 01:42