[hibernate-dev] Session and carrying 3rd party state
Steve Ebersole
steve at hibernate.org
Mon Mar 31 09:46:34 EDT 2014
On Mon, Mar 31, 2014 at 7:48 AM, Gunnar Morling <gunnar at hibernate.org>wrote:
> 2014-03-31 14:35 GMT+02:00 Emmanuel Bernard <emmanuel at 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.
>
>
As I mentioned on IRC weeks ago when we discussed this, there is an
implication here that there be only one registered listener of type Class.
At least conceptually. That can either be an explicit part of the contract
(ala something like org.hibernate.event.service.spi.DuplicationStrategy) or
just an implicit contract for the clients (as the one doing the
registering). The return of this method would simply return the first (or
last) match.
> > 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 <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