I am authoring a java library. Some of the classes that are meant to be used by library users, hold native system resources (over JNI). I'd like to ensure that the user "disposes" these objects, as they are heavy, and in a testsuite they may cause leakage between testcases (for example, I need to ensure TearDown
will dispose). For this purpose I made the Java classes implement AutoCloseable, but this doesn't seem to suffice, or I'm not using it correctly:
I don't see how to use
try-with-resources
statement in the context of tests (I'm usingJUnit5
withMockito
), in that the "resource" is not short-lived - it is part of the test fixture.Being diligent as always, I tried implementing
finalize()
and testing for closure there, but it turns outfinalize()
is not even called (Java10). This is also marked as deprecated and I'm sure this idea will be frowned upon.
How is this done? To be clear, I want the application's tests (that use my library) to fail if they don't call close()
on my objects.
Edit: adding some code if it helps. It's not much, but it's what I'm trying to do.
@SuppressWarnings("deprecation") // finalize() provided just to assert closure (deprecated starting Java 9)
@Override
protected final void finalize() throws Throwable {
if (nativeHandle_ != 0) {
// TODO finalizer is never called, how to assert that close() gets called?
throw new AssertionError("close() was not called; native object leaking");
}
}
Edit2, outcome of bounty Thanks all for replying, half the bounty was automatically awarded. I concluded that for my case it would be best to try the solution involving Cleaner
. However it seems, cleaning actions although registered, are not invoked. I asked a follow-up question here.
If you are interested in consistency in tests, just add method
destroy()
marked by@AfterClass
annotation into test class and close all previously allocated resources in it.If you are interested in an approach that allow you to protect the resource from being not closed, you could provide a way that doesn't expose resource to user explicitly. For example, you code could control resource life cycle and accept only
Consumer<T>
from user.If you can't do that, but still want to be sure that resource will be closed even if user doesn't use it correctly you'll have to do several tricky things. You could split your resource on
sharedPtr
andresource
itself. Then exposesharedPtr
to user and put it into some internal storage wrapped intoWeakReference
. As result of that you'll be able to catch the moment when GC removessharedPtr
and callclose()
on theresource
. Be aware thatresource
must not be exposed to user. I prepared an example, it's not very accurate, but hope it shows the idea:If I were you, I'd do the following:
It also depends whether you want to use this mechanism in production or not - maybe it is worth to add this feature to your lib, because resource management will be a problem in production environment, too. In this case you don't need a wrapper, but you can extend your current classes with this feature. Instead of a teardown, you can use a background thread for regular checks.
Regarding reference types, I recommend this link. PhantomReferences are recommended to use for resource cleanups.
This post does not directly answer your question but provides a different point of view.
One approach to make your clients consistently call
close
is to free them from this responsibility.How can you do it?
Use template pattern.
Sketch implementation
You mentioned that you're working with TCP, so let's assume that you have a
TcpConnection
class that has aclose()
method.Let's define
TcpConnectionOpertaions
interface:and implement it:
TcpConnectionAction
is just a callback, nothing fancy.How the library should be consumed now?
TcpConnectionOperations
interface.For example:
Pros
TcpConnection
TcpConnectionOperations
and mockTcpConnections
and make assertions against themCons
This approach may not work if the lifecycle of a resource is longer than
action
. E.g. it is necessary for the client to keep the resource for a longer time.Then you might want to dive deep in
ReferenceQueue
/Cleaner
(since Java 9) and related API.Inspired by Spring framework
This pattern is widely used in Spring framework.
See for example:
JdbcTemplate
TransactionTemplate
JmsTemplate
.Update 2/7/19
How can I cache/reuse the resource?
This is some kind of pooling:
Some pools in Java:
ConnectionPool
ThreadPoolExecutor
When implementing a pool several questions are raised:
close
d?When the resource should be
close
d?Usually pools provide an explicit
close
method (it may have a different name but the purpose is the same) which closes all the resources held.HikariDataSource#close
ConnectionPool#evictAll
"Close and remove all idle connections in the pool."ConnectionPool#close
ThreadPoolExecutor#shutdown
How it can be shared across multiple threads?
It depends on a kind of the resource itself.
Usually you want to ensure that only one thread accesses one resource.
This can be done using some kind of locking
Demo
Note that code provided here is only for demonstration purposes It has awful performance and violates some OOP principles.
IpAndPort.java
TcpConnection.java
Tests infrastructureCachingTcpConnectionTemplate.java
TcpConnectionOperationsParameterResolver.java
The
ParameterResolver
andAfterAllCallback
are from JUnit.@ReuseTemplate
is a custom annotationReuseTemplate.java
:Finally test:
Running:
Output:
The key observation here is that connections are reused (see "
instance=
")This is oversimplified example of what can be done. Sure, in the real world pooling connections is not so simple. The pool should not grow indefinitely, connections can be kept only for specific period of time and so on. Usually some problems are solved by having something in the background.
Returning to the question
See Junit 5 User Guide. Extension model
You overrode
finalize
so that it throws an exception but they are ignored.See
Object#finalize
The best you can do here is to log the resource leakage and
close
the resourceHow do application tests use your resource? Do they instantiate it using
new
operator? If yes then I think PowerMock can help you (but I'm not sure)If you have hidden instantiation of the resource behind some kind of a factory then you can give the application tests some mock factory
If you're interested you can watch this talk. It's in Russian, but still may be helpful (part of my answer is based on this talk).
In general, if you could reliably test whether a resource has been closed, you could just close it yourself.
The first thing to do is to make handling resource easy for the client. Use the Execute Around idiom.
As far as I know, the only use of execute around for resource handling in the Java library is
java.security.AccessController.doPrivileged
and that's special (the resource is a magic stack frame, that you really don't want to leave open). I believe Spring has long had a much-needed JDBC library for this. I was certainly using execute-around (didn't know it was called that at the time) for JDBC shortly after Java 1.1 made it vaguely practical.The library code should look something like:
(Do get the type parameter declarations in the right place.)
Client side usage looks something like:
Back to testing. How do we make it easy to test even if the testee exfiltrates the resource from the execute around or some other mechanism is available?
The obvious answer is that you provide the execute around solution to the test. You will need to provide some execute around-using API to verify all of your resources that have been acquired in the scope have also been closed. This should be paired with the context the resource is acquired from rather than using global state.
Depending upon which testing framework your clients are using, you may be able to offer something better. JUnit5, for instance, has an annotation-based extension facility which allows you to supply the context as an argument and also apply checks after each test has executed. (But I haven't used it much, so I'm not going to say anything more.)
i would provide instances to those objects through
Factory methods
and with that i have control over the creation of them, and i will feed the consumers withProxies
that does the logic of closing the object