Hi Lukas,
I'll answer backwards.
> I think all of your requirements could still be handled like this
> while not imposing too many constraints on jOOQ?
I don't understand which constraints. The code I gave you allows to
register/unregister listeners, and each factory gets a list which it
can manipulate without caring about threading (it gets its own copy
when it needs to).
> For instance, I
> wouldn't want to synchronize on a static object, as I have no control
> over productive deployments / class-loaders, etc.
Synchronizing on a static object is not a problem as far as I know.
The static object lives in the class loader that contains the jOOQ
classes. Whichever user classes implementing the interface can be used
in this class loader without knowing the actual instance. Only
factories from this class loader can use this list, but this is
desirable (2 jOOQ libraries potentially of different versions loaded
in different class loaders would have their own listeners).
> In a J2EE context,
> it would be very hard to get this correctly within jOOQ.
I don't know what "this" refers to. As I said, I don't understand what
is causing you a problem in the centralised code I gave you. Is it FUD
or do you have a practical problem you know about and that you could
elaborate on?
> The way I see
> it, a list of plugins in every Factory is the optimal solution for
> everyone.
It is a solution, but it is not an optimal solution for 2 reasons:
- it reduces performance for the lazy.
- it complexifies registration of listeners for those who want to
maximize performance.
Let me take your code as I understand it:
> class YourJooqAdapter implements JooqLoggerPlugin {
> static Object LOCK = new Object();
> void event(Configuration c, Query query) {
> if (isListening) {
> synchonized (LOCK) {
> YourMonitoringTool.event(c, query);
> }
> }
> }
> }
An event has a certain number of information. In my tool, these are
the method signatures in the listener:
void debugQueries(SqlQueryDebuggerData sqlQueryDebuggerData);
void debugResultSet(SqlQueryDebuggerData sqlQueryDebuggerData,
SqlQueryDebuggerResultSetData data);
Think of your fetch() call. Because a listener is registered, it would
activate certain debugging features. Once it has all the needed data,
it creates an SqlQueryDebuggerData containing the collected
information (preparation time, execution time, factory, query).
Because a result set is returned, and because we have a listener
(debugging is on), the ResultSet gets wrapped in a special result set
(UsageTrackingResultSet) which collects all the reads/writes, etc.
When the UsageTrackingResultSet close() is called, an event containing
the original SqlQueryDebuggerData and the
SqlQueryDebuggerResultSetData is sent (I send the SqlQueryDebuggerData
so that tools can related the result set data to the query that got
executed and which was notified previously with the original
SqlQueryDebuggerData).
A user does not want this overhead, unless they have at least one
listener registered.
Because you don't like the idea of a central place, your work around
is to always add a listener to the factories, which would ignore all
the events unless it "isListening". The problem is that all the
mechanics I explained above would happen, for nothing, 99.9% of the
time (this is what I called "reducing performance for the lazy").
Advanced Java developers who do not want that overhead would track all
the factories that get created and lost in GC to a central place of
theirs so that when they activate/deactivate logging they add/remove
their listener to all of them. This latter behavior is hard to
implement and error prone whereas the central access is easy for the
library to implement.
For me, logging is a debugging feature mainly because it can have a
cost (in my code, I also have to aggregate parameters used in a batch
for example). It is not something that runs permanently but rather a
way to attach monitoring tools that can perform advanced analysis.
Please let me know if something is unclear.
-Christopher