[hibernate-dev] Session and carrying 3rd party state
Emmanuel Bernard
emmanuel at hibernate.org
Mon Mar 31 11:09:28 EDT 2014
Gunnar, can you lead the work on getting that set of "fleshed out" use
cases. With a bit of luck we could have them by tomorrow for the
meeting and discuss them there.
BTW, as I said in my proposal the delegate does not work as there is not
always a delegate object created.
Emmanuel
On Mon 2014-03-31 9:28, Steve Ebersole wrote:
> Wasn't just me that said -1...
>
> My concerns are 2-fold:
> 1) You want ORM to manage and expose "state storage" on Session even though
> it does not use it.
> 2) You want to store state in there that isnt even Session-scoped. Rather
> you have state that is scoped to a flush cycle, or to a transaction, etc.
>
> Really, as I said in the IRC meeting when we discussed this[1], I really
> just want you guys to think through the use cases and flesh them out. Then
> in my opinion we start to see natural places to hook in state storage. For
> example, in the course of that IRC discussion we saw the need to store
> things at the transaction-level become more clear and it seemed to me like
> storing the state related to these transaction-scoped use-cases is just a
> bad idea plain and simple. You keep it relative to the transaction.
>
> To be honest I still have not seen that "fleshed out" discussion of use
> cases, so its hard for me to say how state storage SHOULD be done. But I
> can certainly see ways that it SHOULD NOT be done.
>
> BTW, in looking through the discussion of getSessionOwner() again, I still
> don't agree that was the right solution for what y'all want/need.
> Ultimately the problem is that SessionImpl, not your delegate, gets passed
> into the listeners/persisters/etc. And that happens because SessionImpl
> passes `this` all the time. A simple solution would be to find all the
> places that SessionImpl passes itself (`this`) into the places of interest
> (creating events, calling persisters, etc) and to instead pass the delegate
> via an override-able method that your delegate overriddes. For example, we
> have this currently on SessionImpl:
>
>
> @Override
> public void persistOnFlush(String entityName, Object object, Map
> copiedAlready) {
> firePersistOnFlush( copiedAlready, new PersistEvent( entityName, object,
> this ) );
> }
>
> but IIUC the following would solve your problems in an even better way:
>
> public EventSource getEventSource() {
> return this;
> }
>
> @Override
> public void persistOnFlush(String entityName, Object object, Map
> copiedAlready) {
> firePersistOnFlush( copiedAlready, new PersistEvent( entityName, object,
> getEventSource() ) );
> }
>
>
> and then your delegate would override this `getEventSource()` method to
> return itself instead.
>
> The premise here is that ultimately we'd be better served actually getting
> the OgmSession passed along to listeners/persisters.
>
>
> [1]
> http://transcripts.jboss.org/meeting/irc.freenode.org/hibernate-dev/2014/hibernate-dev.2014-03-11-15.46.log.html
>
>
>
> On Mon, Mar 31, 2014 at 7:31 AM, Emmanuel Bernard <emmanuel at hibernate.org>wrote:
>
> > 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 <gunnar at hibernate.org> wrote:
> > >
> > >
> > >
> > >
> > > 2014-03-20 23:05 GMT+01:00 Emmanuel Bernard <emmanuel at 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 at lists.jboss.org
> > >> https://lists.jboss.org/mailman/listinfo/hibernate-dev
> > >
> > _______________________________________________
> > hibernate-dev mailing list
> > hibernate-dev at lists.jboss.org
> > https://lists.jboss.org/mailman/listinfo/hibernate-dev
> >
More information about the hibernate-dev
mailing list