Add a Class<T> parameter to the method, in order to avoid an unchecked
cast to the Message type T. The cast was wrapped in a try-catch block,
which is useless due to erasure, so use Class.cast(..) instead.
Refactor all the unchecked casts from Overlay<T> to T into a single
private cast() method. Also add a runtime type check to the constructor
to prevent creation of window objects of the form "A extends Overlay<B>"
for unrelated A & B, as such casts would then subvert the type system.
Remove the no-arg constructor from ActivatableViewAndModel, which sets
a dummy Activatable singleton as the model. (Since the model type param
can't be checked at runtime, improper use of the constructor could cause
heap pollution.)
Instead, extend 'ActivatableView<R, Void>' consistently, as other views
without a model currently do.
The remove code checks to ensure these fields match, but the add code
never did. This could lead to a situation where a MailboxStoragePayload
could be added, but never removed.
Previously, the expire path, the remove path, and the onDisconnect
all used separate logic for updating the map, signaling listeners, and
removing PersistablePaylod objects from the data store. This led to a
bug where the onDisconnect path did not update the protectedDataStore.
Combine the three code paths to ensure that the same state is updated
regardless of the context.
The code to remove expired Entrys in the onDisconnect path was not
correctly removing the Entry from the protectedDataStore.
This patch adds a test that failed and fixes the bug.
* All of this work is done on the UserThread so there is no need to
clone the map.
* ArrayList objects are faster to iterate than HashSets and the data is
guaranteed to be unique since the source is a ConcurrentHashMap
* Finding all items to remove first, then removing them all is an easier
to read code pattern instead of removing during iteration.