Skip to content

Commit

Permalink
OGM-465 Establishing generic event state context;
Browse files Browse the repository at this point in the history
* 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
gunnarmorling authored and DavideD committed Mar 13, 2015
1 parent f731613 commit 8043999
Show file tree
Hide file tree
Showing 8 changed files with 437 additions and 1 deletion.
@@ -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;
}
}
@@ -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;
}
}
@@ -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;
}
}
}
@@ -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;
}
}
}
@@ -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;
}
}
}
@@ -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);
}

0 comments on commit 8043999

Please sign in to comment.