The thing is, the map approach had a big -1 from Steve hanging on its head :)
> 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