Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
OGM-465 Establishing generic event state context;
* The context can be used to propagate state between different components *within* one event cycle * Currently, (auto)-flush and JPA persist are supported * The EventStateInitializer callback is invoked when accessing a given state type for the first time with an event cycle
- Loading branch information
1 parent
f731613
commit 8043999
Showing
8 changed files
with
437 additions
and
1 deletion.
There are no files selected for viewing
94 changes: 94 additions & 0 deletions
94
core/src/main/java/org/hibernate/ogm/dialect/eventstate/impl/EventContextManager.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
/* | ||
* 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 org.hibernate.engine.spi.SessionImplementor; | ||
import org.hibernate.event.spi.EventSource; | ||
import org.hibernate.ogm.util.impl.Immutable; | ||
import org.hibernate.service.Service; | ||
|
||
/** | ||
* 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. If a given state type is accessed for the first time during an event cycle, its associated | ||
* {@link EventStateProducer} will be invoked to obtain a new instance of that 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 { | ||
|
||
private final ThreadLocal<Map<Class<?>, Object>> stateHolder; | ||
|
||
@Immutable | ||
private final Map<Class<?>, EventStateProducer<?>> producers; | ||
|
||
public EventContextManager(Map<Class<?>, EventStateProducer<?>> producers) { | ||
this.stateHolder = new ThreadLocal<>(); | ||
this.producers = Collections.unmodifiableMap( producers ); | ||
} | ||
|
||
void onEventBegin(EventSource session) { | ||
Map<Class<?>, Object> stateMap = new HashMap<>(); | ||
stateMap.put( SessionImplementor.class, session ); | ||
stateHolder.set( stateMap ); | ||
} | ||
|
||
void onEventFinished() { | ||
stateHolder.remove(); | ||
} | ||
|
||
/** | ||
* 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) { | ||
@SuppressWarnings("unchecked") | ||
T value = (T) getStates().get( stateType ); | ||
|
||
if ( value == null ) { | ||
value = create( stateType ); | ||
getStates().put( stateType, value ); | ||
} | ||
|
||
return value; | ||
} | ||
|
||
private <T> T create(Class<T> stateType) { | ||
@SuppressWarnings("unchecked") | ||
EventStateProducer<T> producer = (EventStateProducer<T>) producers.get( stateType ); | ||
|
||
if ( producer == null ) { | ||
throw new IllegalStateException( "No producer found for state type: " + stateType ); | ||
} | ||
|
||
SessionImplementor session = (SessionImplementor) stateHolder.get().get( SessionImplementor.class ); | ||
|
||
return producer.produce( session ); | ||
} | ||
|
||
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; | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
...src/main/java/org/hibernate/ogm/dialect/eventstate/impl/EventContextManagerInitiator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* | ||
* 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.Map; | ||
|
||
import org.hibernate.boot.registry.StandardServiceInitiator; | ||
import org.hibernate.service.spi.ServiceRegistryImplementor; | ||
|
||
/** | ||
* Contributes the {@link EventContextManager} service if needed as per the current configuration. | ||
* | ||
* @author Gunnar Morling | ||
*/ | ||
@SuppressWarnings("rawtypes") | ||
public class EventContextManagerInitiator implements StandardServiceInitiator<EventContextManager> { | ||
|
||
public static final EventContextManagerInitiator INSTANCE = new EventContextManagerInitiator(); | ||
|
||
private EventContextManagerInitiator() { | ||
} | ||
|
||
@Override | ||
public Class<EventContextManager> getServiceInitiated() { | ||
return EventContextManager.class; | ||
} | ||
|
||
@Override | ||
public EventContextManager initiateService(Map configurationValues, ServiceRegistryImplementor registry) { | ||
Map<Class<?>, EventStateProducer<?>> producers = EventStateProducers.getProducers( configurationValues ); | ||
|
||
if ( !producers.isEmpty() ) { | ||
return new EventContextManager( producers ); | ||
} | ||
|
||
return null; | ||
} | ||
} |
68 changes: 68 additions & 0 deletions
68
...org/hibernate/ogm/dialect/eventstate/impl/EventContextManagingAutoFlushEventListener.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/* | ||
* 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 org.hibernate.HibernateException; | ||
import org.hibernate.event.service.spi.DuplicationStrategy; | ||
import org.hibernate.event.spi.AutoFlushEvent; | ||
import org.hibernate.event.spi.AutoFlushEventListener; | ||
import org.hibernate.ogm.util.impl.EffectivelyFinal; | ||
|
||
/** | ||
* Delegating {@link AutoFlushEventListener} which manages the {@link EventContextManager}. | ||
* | ||
* @author Gunnar Morling | ||
*/ | ||
public class EventContextManagingAutoFlushEventListener implements AutoFlushEventListener { | ||
|
||
@EffectivelyFinal | ||
private AutoFlushEventListener delegate; | ||
private final EventContextManager stateManager; | ||
|
||
public EventContextManagingAutoFlushEventListener(EventContextManager stateManager) { | ||
this.stateManager = stateManager; | ||
} | ||
|
||
public void setDelegate(AutoFlushEventListener delegate) { | ||
this.delegate = delegate; | ||
} | ||
|
||
@Override | ||
public void onAutoFlush(AutoFlushEvent event) throws HibernateException { | ||
stateManager.onEventBegin( event.getSession() ); | ||
|
||
try { | ||
delegate.onAutoFlush( event ); | ||
} | ||
finally { | ||
stateManager.onEventFinished(); | ||
} | ||
} | ||
|
||
public static class EventContextManagingAutoFlushEventListenerDuplicationStrategy implements DuplicationStrategy { | ||
|
||
public static final DuplicationStrategy INSTANCE = new EventContextManagingAutoFlushEventListenerDuplicationStrategy(); | ||
|
||
private EventContextManagingAutoFlushEventListenerDuplicationStrategy() { | ||
} | ||
|
||
@Override | ||
public boolean areMatch(Object listener, Object original) { | ||
if ( listener instanceof EventContextManagingAutoFlushEventListener && original instanceof AutoFlushEventListener ) { | ||
( (EventContextManagingAutoFlushEventListener) listener ).setDelegate( (AutoFlushEventListener) original ); | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
@Override | ||
public Action getAction() { | ||
return Action.REPLACE_ORIGINAL; | ||
} | ||
} | ||
} |
68 changes: 68 additions & 0 deletions
68
...ava/org/hibernate/ogm/dialect/eventstate/impl/EventContextManagingFlushEventListener.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/* | ||
* 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 org.hibernate.HibernateException; | ||
import org.hibernate.event.service.spi.DuplicationStrategy; | ||
import org.hibernate.event.spi.FlushEvent; | ||
import org.hibernate.event.spi.FlushEventListener; | ||
import org.hibernate.ogm.util.impl.EffectivelyFinal; | ||
|
||
/** | ||
* Delegating {@link FlushEventListener} which manages the {@link EventContextManager}. | ||
* | ||
* @author Gunnar Morling | ||
*/ | ||
public class EventContextManagingFlushEventListener implements FlushEventListener { | ||
|
||
@EffectivelyFinal | ||
private FlushEventListener delegate; | ||
private final EventContextManager stateManager; | ||
|
||
public EventContextManagingFlushEventListener(EventContextManager stateManager) { | ||
this.stateManager = stateManager; | ||
} | ||
|
||
public void setDelegate(FlushEventListener delegate) { | ||
this.delegate = delegate; | ||
} | ||
|
||
@Override | ||
public void onFlush(FlushEvent event) throws HibernateException { | ||
stateManager.onEventBegin( event.getSession() ); | ||
|
||
try { | ||
delegate.onFlush( event ); | ||
} | ||
finally { | ||
stateManager.onEventFinished(); | ||
} | ||
} | ||
|
||
public static class EventContextManagingFlushEventListenerDuplicationStrategy implements DuplicationStrategy { | ||
|
||
public static final DuplicationStrategy INSTANCE = new EventContextManagingFlushEventListenerDuplicationStrategy(); | ||
|
||
private EventContextManagingFlushEventListenerDuplicationStrategy() { | ||
} | ||
|
||
@Override | ||
public boolean areMatch(Object listener, Object original) { | ||
if ( listener instanceof EventContextManagingFlushEventListener && original instanceof FlushEventListener ) { | ||
( (EventContextManagingFlushEventListener) listener ).setDelegate( (FlushEventListener) original ); | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
@Override | ||
public Action getAction() { | ||
return Action.REPLACE_ORIGINAL; | ||
} | ||
} | ||
} |
83 changes: 83 additions & 0 deletions
83
...a/org/hibernate/ogm/dialect/eventstate/impl/EventContextManagingPersistEventListener.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
/* | ||
* 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.Map; | ||
|
||
import org.hibernate.HibernateException; | ||
import org.hibernate.event.service.spi.DuplicationStrategy; | ||
import org.hibernate.event.spi.PersistEvent; | ||
import org.hibernate.event.spi.PersistEventListener; | ||
import org.hibernate.jpa.event.internal.core.JpaPersistEventListener; | ||
import org.hibernate.ogm.util.impl.EffectivelyFinal; | ||
|
||
/** | ||
* Delegating {@link PersistEventListener} which manages the {@link EventContextManager}. | ||
* | ||
* @author Gunnar Morling | ||
*/ | ||
public class EventContextManagingPersistEventListener implements PersistEventListener { | ||
|
||
@EffectivelyFinal | ||
private PersistEventListener delegate; | ||
private final EventContextManager stateManager; | ||
|
||
public EventContextManagingPersistEventListener(EventContextManager stateManager) { | ||
this.stateManager = stateManager; | ||
} | ||
|
||
public void setDelegate(PersistEventListener delegate) { | ||
this.delegate = delegate; | ||
} | ||
|
||
@Override | ||
public void onPersist(PersistEvent event) throws HibernateException { | ||
stateManager.onEventBegin( event.getSession() ); | ||
|
||
try { | ||
delegate.onPersist( event ); | ||
} | ||
finally { | ||
stateManager.onEventFinished(); | ||
} | ||
} | ||
|
||
@Override | ||
public void onPersist(PersistEvent event, Map createdAlready) throws HibernateException { | ||
stateManager.onEventBegin( event.getSession() ); | ||
|
||
try { | ||
delegate.onPersist( event, createdAlready ); | ||
} | ||
finally { | ||
stateManager.onEventFinished(); | ||
} | ||
} | ||
|
||
public static class EventContextManagingPersistEventListenerDuplicationStrategy implements DuplicationStrategy { | ||
|
||
public static final DuplicationStrategy INSTANCE = new EventContextManagingPersistEventListenerDuplicationStrategy(); | ||
|
||
private EventContextManagingPersistEventListenerDuplicationStrategy() { | ||
} | ||
|
||
@Override | ||
public boolean areMatch(Object listener, Object original) { | ||
if ( listener instanceof EventContextManagingPersistEventListener && original instanceof JpaPersistEventListener ) { | ||
( (EventContextManagingPersistEventListener) listener ).setDelegate( (JpaPersistEventListener) original ); | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
@Override | ||
public Action getAction() { | ||
return Action.REPLACE_ORIGINAL; | ||
} | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
core/src/main/java/org/hibernate/ogm/dialect/eventstate/impl/EventStateProducer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
/* | ||
* 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 org.hibernate.engine.spi.SessionImplementor; | ||
|
||
/** | ||
* Callback for producing event cycle scoped state objects. | ||
* <p> | ||
* Invoked by {@link EventContextManager} in case a event state type is accessed for the first time during a given event | ||
* cycle. | ||
* | ||
* @author Gunnar Morling | ||
*/ | ||
public interface EventStateProducer<T> { | ||
|
||
T produce(SessionImplementor session); | ||
} |
Oops, something went wrong.