2014-03-31 14:35 GMT+02:00 Emmanuel Bernard <emman...@hibernate.org>:
> Btw to answer your concern about the event listeners and the use of > instance of, we could imagine an API like getListenerOfType(Class) on the > SessionEventListenerManager. > Hum, couldn't there be several instances of the same listener type, e.g. configured differently? Maybe not in our particular case, but such API would restrict this case. > Because one key thing that this approach solves and that the map approach > does not is the actual event listener callback capability on close, flush, > etc > Yes, the map based approach would still need a listener which does these clean-up things. > > On 31 mars 2014, at 12:07, Gunnar Morling <gun...@hibernate.org> wrote: > > > > > 2014-03-20 23:05 GMT+01:00 Emmanuel Bernard <emman...@hibernate.org>: > >> I took some more time to think about our conversation from 2 IRC meeting >> ago >> about offering the ability to carry session bound state not related to >> ORM per se. >> Below is a sum and a potential solution. >> If you are short on time, read Goals, then the SessionSessionEventListener >> approach and ignore the rest. >> >> ## Goals >> >> The goal is to be able to carry session bound state for non-ORM projects >> like search and OGM. >> We want to avoid ThreadLocal use, in particular when it cannot be >> protected by a try / catch for proper resource cleaning. >> We want to avoid a structure that would be shared across threads >> concurrently >> i.e. using ConcurrentHashMap with a Weak reference to the session. >> It needs to be informed of a call to session.clear() >> It needs to be informed of a call to session.close() >> The state needs to be accessed from event listener implementations and >> custom >> persister / loader implementations i.e. SessionImplementor and maybe >> EventSource? >> >> ## Approaches >> >> I'll discuss the approaches we explored in the meeting and then offer an >> alternative one that I think is pretty interesting and fit better with >> the current Session model. >> >> ### Map >> >> This is essentially sticking a map on SessionImpl and use it to carry >> state. >> The following is a pseudo implementation >> >> >> /** >> * interface implemented by SessionImpl and the like >> */ >> interface SessionCompanion { >> Object getCompanion(String key); >> void addCompanion(String key, Object companion); >> void removeCompanion(String key); >> } >> >> >> /** >> * adds a map to the SessionImpl >> */ >> SessionImpl { >> private Map<String, Object> companions; >> public Object getCompanion(String key) { return >> companions.get(key); } >> public void addCompanion(String key, Object value) { >> companions.add(key, companion); } >> public void removeCompanion(String key) { companions.remove(key) } >> } >> >> The persister or event listener would call SessionCompation.*Companion >> method >> to put and retrieve its state. >> >> There is no clear / close event listener loop and it would need to be >> added. >> >> ### Delegator >> >> Gunnar and teve discussed an approach where the delegator would be passed >> to >> the underlying session and be accessible via an `unwrap` method. >> I have not followed the details but this approach has one major flaw: the >> delegator (OgmSession, FullTextSession etc) is not always created and thus >> would not be necessarily available. >> A somewhat similar idea involving passing the session owner has the same >> drawback. And another one described by Gunnar in >> https://hibernate.atlassian.net/browse/OGM-469 >> >> ### The type-safe map approach >> >> This approach is vaguely similar to the Map approach except that the >> payload is >> represented and looked up by Class. This has the benefit of not having >> namespace problems and is generally less String-y. >> >> >> /** >> * interface implemented by SessionImpl and the like >> */ >> interface SessionCompanion { >> T getCompanion(Class<T> type); >> void addCompanion(Object companion); >> void removeCompanion(Class<?> type) >> >> } >> >> >> SessionImpl { >> //could also use an array or an ArrayList >> private Map<Class<?>, Object> companions; >> public T getCompanion(Class<T> type) { return (T) >> companions.get(type); } >> public void addCompanion(Object companion) { >> companions.add(companion.getClass(), type); } >> public void removeCompanion(Class<T> type) { >> companions.remove(type); } >> } >> >> Like in the Map approach, the persister or custom event listener would >> interact >> with SessionCompanion. >> There are open issues like what should be done when two objects of the >> same >> type are added to the same session. >> Likewise the clear / close hook issues need to be addressed. >> >> ### the SessionEventListener approach >> >> I did not know but there is a concept of `SessionEventListener` which can >> be >> added to a `SessionImplementor`. It has hooks that are addressing most of >> the >> goals. >> >> >> //interface already exists >> interface SessionImplementor { >> public SessionEventListenerManager getEventListenerManager(); >> } >> >> //interface already exists >> public interface SessionEventListenerManager extends >> SessionEventListener { >> // add this method to be able to retrieve a specific listener >> holding some state for a 3rd party project >> List<SessionEventListener> getSessionEventListeners(); >> } >> >> OGM or Search would implement a `SessionEventListener` and attach an >> instance to a session via `Session.addEventListeners()`. >> It would require to add a method to retrieve the list of >> `SessionEventListener`s attached to a `SessionEventListenerManager`. >> >> List<SessionEventListeners> listeners = >> sessionImplementor.getSessionEventListenerManager().getEnlistedListeners(); >> OgmSessionEventListener ogmListener = >> findOrAddOgmListener(sessionImplementor, listeners); >> ogmListener.someStuff(); >> >> ## What about clear and close? >> >> We have a few ways to react to these. >> SessionEventListener is already called back when a flush begins / ends as >> well as when Session closes. >> We need to either: >> >> - add a clear begins / ends callback >> - have the third party project add a ClearEventListener which would >> access the SessionEventListeners and do some magic. >> >> The first approach has my preference and would do: >> >> >> public interface SessionEventListener { >> [...] >> void clearStart(); >> void clearEnd(); >> } >> >> What do you guys think? The SessionEventListener approach feels more >> natural. >> > > Tbh. I feel maintaining the state with a listener is a bit hacky. How > would we find "our" listener, I guess it would involve some ugly instanceof > check? > > The map based approaches seem preferable to me (I dislike the "companion" > naming scheme though). The type-safe map approach is nice, as you say it > may cause type collisions though and lead to a proliferation of key types. > How about a combined approach which provides one such typesafe container > per "client": > > public interface SessionAttributes { > > <T> T get(Class<T> type); > <T> T put(Class<T> type, T value); > <T> T remove(Class<T> type); > } > > And > > public interface SessionImplementor { > ... > SessionAttributes getAttributes(String integratorId); > } > > And in OGM: > > public static final String OGM_COMMON_ATTRIBUTES = > "org.hibernate.ogm.attributes.common"; > ... > FooBar fb = > session.getAttributes(OGM_COMMON_ATTRIBUTES).get(FooBar.class); > > This avoids collisions of attributes of the same type between different > clients such as OGM and Search (there could even be several attribute > containers used by one client). The SessionAttributes could be instantiated > lazily upon the first getAttributes() call, avoiding any overhead if the > functionality is not used. > > The clean-up part (if it is required?) could then be done by an > accompanying SessionEventListener. > > --Gunnar > > >> >> Emmanuel >> _______________________________________________ >> hibernate-dev mailing list >> hibernate-dev@lists.jboss.org >> https://lists.jboss.org/mailman/listinfo/hibernate-dev >> > > _______________________________________________ hibernate-dev mailing list hibernate-dev@lists.jboss.org https://lists.jboss.org/mailman/listinfo/hibernate-dev