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

Reply via email to