/* * Hibernate OGM, Domain model persistence for NoSQL datastores * * License: GNU Lesser General Public License (LGPL), version 2.1 or later * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.ogm.dialect.eventstate.impl; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.event.spi.EventSource; import org.hibernate.ogm.util.impl.EffectivelyFinal; import org.hibernate.ogm.util.impl.Immutable; import org.hibernate.service.Service; import org.hibernate.service.spi.ServiceRegistryAwareService; import org.hibernate.service.spi.ServiceRegistryImplementor; /** * A service which provides access to state specific to one event cycle (currently (auto)-flush or persist). * <p> * Client code (such as persisters, dialects etc.) may use this service to propagate state amongst each other, as long * as they are within the same event cycle. States are identified by class objects which are used as key when accessing * the contextual map. At event cycle begin, all enabled {@link EventStateLifecycle}s will be invoked to obtain a new * instance of their state type. * <p> * Accessing the context when not being within the scope of a supported event cycle is illegal. * <p> * The service state is managed by listeners such as {@link EventContextManagingAutoFlushEventListener} which make sure * that the context is destroyed upon event cycle completion. * * @author Gunnar Morling */ public class EventContextManager implements Service, ServiceRegistryAwareService { private final ThreadLocal<Map<Class<?>, Object>> stateHolder; @Immutable @EffectivelyFinal private Map<Class<?>, EventStateLifecycle<?>> enabledLifecycles; public EventContextManager() { this.stateHolder = new ThreadLocal<>(); } @Override public void injectServices(ServiceRegistryImplementor serviceRegistry) { this.enabledLifecycles = Collections.unmodifiableMap( EventStateLifecycles.INSTANCE.getEnabledLifecycles( serviceRegistry ) ); } /** * Whether any components will make use of the event context or not. */ public static boolean isEventContextRequired(ServiceRegistryImplementor serviceRegistry) { return !EventStateLifecycles.INSTANCE.getEnabledLifecycles( serviceRegistry ).isEmpty(); } void onEventBegin(EventSource session) { Map<Class<?>, Object> stateMap = new HashMap<>(); stateMap.put( SessionImplementor.class, session ); for ( Entry<Class<?>, EventStateLifecycle<?>> lifecycle : enabledLifecycles.entrySet() ) { Object value = lifecycle.getValue().create( session ); stateMap.put( lifecycle.getKey(), value ); } stateHolder.set( stateMap ); } void onEventFinished() { Map<Class<?>, Object> states = stateHolder.get(); if ( states == null ) { return; } SessionImplementor session = (SessionImplementor) states.get( SessionImplementor.class ); for ( Entry<Class<?>, Object> state : states.entrySet() ) { if ( state.getValue() != session ) { onFinish( state.getKey(), state.getValue(), session ); } } stateHolder.remove(); } private <T> void onFinish(Class<T> stateType, Object state, SessionImplementor session) { @SuppressWarnings("unchecked") T typedState = (T) state; getLifecycle( stateType ).onFinish( typedState, session ); } /** * Returns the state object of the given type. * <p> * <b>Note:</b> Must only be called when being within a supported event cycle. */ public <T> T get(Class<T> stateType) { Map<Class<?>, Object> states = getStates(); T value = getState( states, stateType ); if ( value == null ) { throw new IllegalArgumentException( "Accessing state of type not enabled: " + stateType ); } return value; } /** * Whether the event context currently is active (i.e. we are within a supported event cycle such as flush, persist) * or not. */ public boolean isActive() { return stateHolder.get() != null; } private Map<Class<?>, Object> getStates() { Map<Class<?>, Object> states = stateHolder.get(); if ( states == null ) { throw new IllegalStateException( "Must not access event cycle state if not within a supported event cycle " + "(flush, auto-flush, persist)" ); } return states; } private <T> T getState(Map<Class<?>, Object> states, Class<T> stateType) { @SuppressWarnings("unchecked") T value = (T) states.get( stateType ); return value; } private <T> EventStateLifecycle<T> getLifecycle(Class<T> stateType) { @SuppressWarnings("unchecked") EventStateLifecycle<T> lifecycle = (EventStateLifecycle<T>) enabledLifecycles.get( stateType ); return lifecycle; } }