final class ChangeBroker extends java.lang.Object implements Scopeable
RecordBuffer
to defer creating a placeholder
snapshot as long as possible;
Persistence
query methods to disable auto-flushing
of the active Hibernate session when possible;
AdaptiveQuery
to invalidate its current result set
when necessary.
RecordChangeListener
objects register to receive notifications for one or
more DMO types, using the addListener(com.goldencode.p2j.persist.event.RecordChangeListener)
method. A listener will
only receive notifications for changes to DMOs of the types for which they
registered. Beyond this, no other filtering is performed. Listeners are
maintained in a scoped dictionary, so there is never a need to remove a
listener explicitly; when the scope in which the listener was registered
is closed, it is released automatically.
Change notifications are driven by the stateChanged(com.goldencode.p2j.persist.event.RecordChangeEvent.Type, com.goldencode.p2j.persist.RecordBuffer, com.goldencode.p2j.persist.Record, com.goldencode.p2j.persist.Record, java.util.Map<java.lang.String, java.util.List<java.lang.Integer>>)
method. It
is anticipated that this only will be invoked by RecordBuffer
objects, as user code calls setter methods on DMO proxies. A state change
causes a RecordChangeEvent
to be broadcast to all listeners registered against the
type of DMO which was modified. "Type" in this sense corresponds to the
DMO interface.
Because it relies upon the RecordBuffer
as the source for all
state change events, ChangeBroker
is not useful in non-legacy
client contexts (i.e., where external code accesses the services of the
Persistence
class directly). For the most part, this class
is transparent for these contexts. One exception is that the auto-flush
optimization technique described above would be harmful if allowed to
proceed naturally, because flushing would always be disabled since state
changes would never be reported, thus it would always seem flushing is not
required. To prevent this problem, this class detects when it is not in
legacy mode and disables this optimization accordingly in such cases.
Modifier and Type | Class and Description |
---|---|
private static class |
ChangeBroker.ListenerSet
An ordered set of listeners (iterates in the order added to the set), which optionally
links to other sets of listeners up and down the stack of open scopes.
|
Modifier and Type | Field and Description |
---|---|
private static ContextLocal<ChangeBroker> |
context
Context local instance of this class
|
private ScopedDictionary<java.lang.Class<?>,ChangeBroker.ListenerSet> |
listeners
Sets of listeners which have registered interest in DMO property changes
|
private static java.util.logging.Logger |
LOG
Logger
|
private ProcedureManager.ProcedureHelper |
pm
Helper to use the ProcedureManager without any context local lookups
|
private java.util.Map<java.lang.Object,java.util.Map<java.lang.Class<?>,ChangeBroker.ListenerSet>> |
procListeners
Map of saved listeners, per persistent procedure referent.
|
private TransactionManager.TransactionHelper |
tm
Helper to use the TM without any context local lookups
|
Modifier | Constructor and Description |
---|---|
private |
ChangeBroker()
This class can only be constructed privately.
|
Modifier and Type | Method and Description |
---|---|
(package private) void |
addListener(RecordChangeListener listener)
Register a listener to receive DMO property change notifications.
|
(package private) void |
addListener(RecordChangeListener listener,
int depth)
Register a listener to receive DMO property change notifications.
|
(package private) static ChangeBroker |
get()
Retrieve the context-local instance of this class, instantiating it
first if necessary.
|
(package private) static void |
initialize()
Register with the
TransactionManager a factory object which creates instances of this
class, so that they can be registered to receive notifications of
runtime scope start and finish events. |
(package private) boolean |
isQueried(java.lang.Class<? extends DataModelObject> dmoIface,
java.lang.Integer multiplexID)
Check if there are any listeners registered with a query, for this temp-table, identified by its DMO
interface and the multiplex ID.
|
(package private) boolean |
removeFromGlobal(RecordChangeListener listener)
Remove a listener from global scope.
|
(package private) void |
removeListener(RecordChangeListener listener)
Remove the specified listener from all scopes.
|
void |
scopeDeleted()
Notification that the instantiating procedure where the scope was opened was deleted.
|
void |
scopeFinished()
Pop the current scope from the listeners dictionary whenever a runtime
scope is closed.
|
void |
scopeStart()
Add a scope to the listeners dictionary whenever a new runtime scope
opens.
|
(package private) void |
stateChanged(RecordChangeEvent.Type type,
RecordBuffer buffer,
Record dmo,
Record snapshot,
java.util.Map<java.lang.String,java.util.List<java.lang.Integer>> properties)
Broadcast a notification that a DMO's state has changed.
|
private static final java.util.logging.Logger LOG
private static final ContextLocal<ChangeBroker> context
private final ProcedureManager.ProcedureHelper pm
private final TransactionManager.TransactionHelper tm
private final ScopedDictionary<java.lang.Class<?>,ChangeBroker.ListenerSet> listeners
private final java.util.Map<java.lang.Object,java.util.Map<java.lang.Class<?>,ChangeBroker.ListenerSet>> procListeners
private ChangeBroker()
static ChangeBroker get()
static void initialize()
TransactionManager
a factory object which creates instances of this
class, so that they can be registered to receive notifications of
runtime scope start and finish events.
This method should be invoked once during the server bootstrap phase.
public void scopeStart()
scopeStart
in interface Scopeable
public void scopeFinished()
scopeFinished
in interface Scopeable
public void scopeDeleted()
This implementation is a no-op.
scopeDeleted
in interface Scopeable
void addListener(RecordChangeListener listener)
This method is tolerant of being called multiple times for the same listener in the same scope; the listener will only be registered once, and therefore will receive only one notification per distinct event.
listener
- Listener object to be added.void addListener(RecordChangeListener listener, int depth)
depth
parameter. The
listener will receive notifications of any changes to DMOs of the types
for which it is registered, as they occur in the specified scopes or in
the nested ones. The listener is deregistered automatically
when the scopes end.
When we call this function with a depth
less than the
number of innermost scope, we actually want to register it in some outer
scope, but since registrations at inner scopes will hide those at outer
scopes during a lookup, we should register it in the several scopes
if needed.
This method is tolerant of being called multiple times for the same listener in the same scope; the listener will only be registered once, and therefore will receive only one notification per distinct event.
If the buffer is opened in a procedure executed persistent, the buffer has to survive when the procedure code is finished until the persistent procedure is deleted. In this case the buffer is stored in the GLOBAL scope (depth = 0).
listener
- Listener object to be added.depth
- 1-based number of the most outer scope in which we want to register the listener
(starting from the very outermost scope). Use 0 for GLOBAL scope when the buffer
is opened in a persistent procedure.boolean removeFromGlobal(RecordChangeListener listener)
If this buffer was not open in global scope (from a persistent procedure) this is a no-op and
the returned value is false
.
listener
- The listener to be removed.true
if the listener was successfully removed from global scope.void removeListener(RecordChangeListener listener)
listener
- The listener to be removed.boolean isQueried(java.lang.Class<? extends DataModelObject> dmoIface, java.lang.Integer multiplexID)
dmoIface
- Data model object interface.multiplexID
- The multiplex ID for the temp-table reclaiming this key.void stateChanged(RecordChangeEvent.Type type, RecordBuffer buffer, Record dmo, Record snapshot, java.util.Map<java.lang.String,java.util.List<java.lang.Integer>> properties) throws PersistenceException
buffer
will receive the event.type
- Event type: insert, update, or delete.buffer
- Record buffer which originated the event.dmo
- Post-modification state of the affected DMO (in the
case where the modification is the deletion of the DMO from
the database, this will be the object instance which was
deleted).snapshot
- A deep copy of the DMO which reflects the state of the DMO as
it was first set into the record buffer. For a newly created
record, this will contain the primary key assigned to the
record. All other properties will be set to their default
values.properties
- A map whose keys are the names of those properties which have
been modified and whose values are lists of the indexes at
which those values are stored (indexed properties only). For
a simple property, the matching value will be
null
.PersistenceException
- if any error occurs within a listener while processing a
change event. Exceptions will be deferred until all listeners
have had a chance to process the event. If more than one
listener throws an exception, only the first will be rethrown
by this method; however, all will be logged.