diff --git a/src/main/java/de/thm/arsnova/event/AfterFullUpdateEvent.java b/src/main/java/de/thm/arsnova/event/AfterFullUpdateEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..3d944d51cfd05c80084d9b73dba062883f657d38 --- /dev/null +++ b/src/main/java/de/thm/arsnova/event/AfterFullUpdateEvent.java @@ -0,0 +1,16 @@ +package de.thm.arsnova.event; + +import de.thm.arsnova.model.Entity; + +public class AfterFullUpdateEvent<E extends Entity> extends AfterUpdateEvent<E> { + private final E oldEntity; + + public AfterFullUpdateEvent(final Object source, final E entity, final E oldEntity) { + super(source, entity); + this.oldEntity = oldEntity; + } + + public E getOldEntity() { + return oldEntity; + } +} diff --git a/src/main/java/de/thm/arsnova/event/AfterPatchEvent.java b/src/main/java/de/thm/arsnova/event/AfterPatchEvent.java index 4b73660d36aef3cb763d09f099dbc81535c8a700..847bca9e9396ed56d3c244ed5eecf41bc9484e78 100644 --- a/src/main/java/de/thm/arsnova/event/AfterPatchEvent.java +++ b/src/main/java/de/thm/arsnova/event/AfterPatchEvent.java @@ -2,8 +2,17 @@ package de.thm.arsnova.event; import de.thm.arsnova.model.Entity; +import java.util.Map; + public class AfterPatchEvent<E extends Entity> extends AfterUpdateEvent<E> { - public AfterPatchEvent(final Object source, final E entity) { + private final Map<String, Object> changes; + + public AfterPatchEvent(final Object source, final E entity, final Map<String, Object> changes) { super(source, entity); + this.changes = changes; + } + + public Map<String, Object> getChanges() { + return changes; } } diff --git a/src/main/java/de/thm/arsnova/event/AfterUpdateEvent.java b/src/main/java/de/thm/arsnova/event/AfterUpdateEvent.java index 702173630447bc819fe8bf66771a64c56e8b584d..7920b79d220fc72ce48543d3d9997fdaebb65035 100644 --- a/src/main/java/de/thm/arsnova/event/AfterUpdateEvent.java +++ b/src/main/java/de/thm/arsnova/event/AfterUpdateEvent.java @@ -2,7 +2,7 @@ package de.thm.arsnova.event; import de.thm.arsnova.model.Entity; -public class AfterUpdateEvent<E extends Entity> extends CrudEvent<E> { +public abstract class AfterUpdateEvent<E extends Entity> extends CrudEvent<E> { public AfterUpdateEvent(final Object source, final E entity) { super(source, entity); } diff --git a/src/main/java/de/thm/arsnova/event/BeforeFullUpdateEvent.java b/src/main/java/de/thm/arsnova/event/BeforeFullUpdateEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..8514163395a1679925eecc3dd48c42f76fe0fbbe --- /dev/null +++ b/src/main/java/de/thm/arsnova/event/BeforeFullUpdateEvent.java @@ -0,0 +1,16 @@ +package de.thm.arsnova.event; + +import de.thm.arsnova.model.Entity; + +public class BeforeFullUpdateEvent<E extends Entity> extends BeforeUpdateEvent<E> { + private final E oldEntity; + + public BeforeFullUpdateEvent(final Object source, final E entity, final E oldEntity) { + super(source, entity); + this.oldEntity = oldEntity; + } + + public E getOldEntity() { + return oldEntity; + } +} diff --git a/src/main/java/de/thm/arsnova/event/BeforePatchEvent.java b/src/main/java/de/thm/arsnova/event/BeforePatchEvent.java index 82eee8540dd42629a35834dbb7f365e962599fb5..717d14f98f82b781bb3152d842947248380064a3 100644 --- a/src/main/java/de/thm/arsnova/event/BeforePatchEvent.java +++ b/src/main/java/de/thm/arsnova/event/BeforePatchEvent.java @@ -2,8 +2,17 @@ package de.thm.arsnova.event; import de.thm.arsnova.model.Entity; +import java.util.Map; + public class BeforePatchEvent<E extends Entity> extends BeforeUpdateEvent<E> { - public BeforePatchEvent(final Object source, final E entity) { + private final Map<String, Object> changes; + + public BeforePatchEvent(final Object source, final E entity, final Map<String, Object> changes) { super(source, entity); + this.changes = changes; + } + + public Map<String, Object> getChanges() { + return changes; } } diff --git a/src/main/java/de/thm/arsnova/event/BeforeUpdateEvent.java b/src/main/java/de/thm/arsnova/event/BeforeUpdateEvent.java index 65fc96e1009ca9a1423fbe596049af898124617f..f5964dce95cff6844548cbb79cecd531cec26d43 100644 --- a/src/main/java/de/thm/arsnova/event/BeforeUpdateEvent.java +++ b/src/main/java/de/thm/arsnova/event/BeforeUpdateEvent.java @@ -2,7 +2,7 @@ package de.thm.arsnova.event; import de.thm.arsnova.model.Entity; -public class BeforeUpdateEvent<E extends Entity> extends CrudEvent<E> { +public abstract class BeforeUpdateEvent<E extends Entity> extends CrudEvent<E> { public BeforeUpdateEvent(final Object source, final E entity) { super(source, entity); } diff --git a/src/main/java/de/thm/arsnova/event/StateChangeEvent.java b/src/main/java/de/thm/arsnova/event/StateChangeEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..69b55798117cdac601dfb75723f6d7678daf448e --- /dev/null +++ b/src/main/java/de/thm/arsnova/event/StateChangeEvent.java @@ -0,0 +1,45 @@ +package de.thm.arsnova.event; + +import de.thm.arsnova.model.Entity; +import org.springframework.context.ApplicationEvent; +import org.springframework.core.ResolvableType; +import org.springframework.core.ResolvableTypeProvider; + +import java.util.Optional; + +public class StateChangeEvent<E extends Entity, T> extends ApplicationEvent implements ResolvableTypeProvider { + private final E entity; + private final String stateName; + private final T newValue; + private final T oldValue; + + public StateChangeEvent(final Object source, final E entity, final String stateName, + final T newValue, final T oldValue) { + super(source); + this.entity = entity; + this.stateName = stateName; + this.newValue = newValue; + this.oldValue = oldValue; + } + + public E getEntity() { + return entity; + } + + public String getStateName() { + return stateName; + } + + public T getNewValue() { + return newValue; + } + + public Optional<T> getOldValue() { + return Optional.ofNullable(oldValue); + } + + @Override + public ResolvableType getResolvableType() { + return ResolvableType.forClassWithGenerics(getClass(), entity.getClass(), newValue.getClass()); + } +} diff --git a/src/main/java/de/thm/arsnova/event/StateEventDispatcher.java b/src/main/java/de/thm/arsnova/event/StateEventDispatcher.java new file mode 100644 index 0000000000000000000000000000000000000000..7d4993ad65723945489c9f9ae0ecf5268ea1dbd7 --- /dev/null +++ b/src/main/java/de/thm/arsnova/event/StateEventDispatcher.java @@ -0,0 +1,75 @@ +package de.thm.arsnova.event; + +import de.thm.arsnova.model.Content; +import de.thm.arsnova.model.Entity; +import de.thm.arsnova.model.Room; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationEventPublisherAware; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.function.Function; + +/** + * StateEventDispatcher publishes additional, more specific events for state changes of entities when + * {@link AfterUpdateEvent}s are received. + * + * @author Daniel Gerhardt + */ +@Component +public class StateEventDispatcher implements ApplicationEventPublisherAware { + private ApplicationEventPublisher eventPublisher; + + @EventListener + public void dispatchRoomStateEvent(final AfterFullUpdateEvent<Room> event) { + final Room newRoom = event.getEntity(); + final Room oldRoom = event.getOldEntity(); + publishEventIfPropertyChanged(newRoom, oldRoom, Room::isClosed, "closed"); + publishEventIfPropertyChanged(newRoom, oldRoom, Room::getSettings, "settings"); + } + + @EventListener + public void dispatchRoomStateEvent(final AfterPatchEvent<Room> event) { + final Room room = event.getEntity(); + final Map<String, Object> changes = event.getChanges(); + publishEventIfPropertyChanged(room, changes, "closed", "closed"); + publishEventIfPropertyChanged(room, changes, "settings", "settings"); + } + + @EventListener + public void dispatchContentStateEvent(final AfterFullUpdateEvent<Content> event) { + final Content newContent = event.getEntity(); + final Content oldContent = event.getOldEntity(); + publishEventIfPropertyChanged(newContent, oldContent, Content::getState, "state"); + } + + @EventListener + public void dispatchContentStateEvent(final AfterPatchEvent<Content> event) { + final Content content = event.getEntity(); + final Map<String, Object> changes = event.getChanges(); + publishEventIfPropertyChanged(content, changes, "state", "state"); + } + + private <E extends Entity, T extends Object> void publishEventIfPropertyChanged( + final E newEntity, final E oldEntity, final Function<E, T> propertyGetter, final String stateName) { + T newValue = propertyGetter.apply(newEntity); + T oldValue = propertyGetter.apply(oldEntity); + if (!newValue.equals(oldValue)) { + eventPublisher.publishEvent(new StateChangeEvent<>(this, newEntity, stateName, newValue, oldValue)); + } + } + + private <E extends Entity> void publishEventIfPropertyChanged( + final E entity, final Map<String, Object> changes, final String property, final String stateName) { + if (changes.containsKey(property)) { + eventPublisher.publishEvent( + new StateChangeEvent<>(this, entity, stateName, changes.get(property), null)); + } + } + + @Override + public void setApplicationEventPublisher(final ApplicationEventPublisher applicationEventPublisher) { + this.eventPublisher = applicationEventPublisher; + } +} diff --git a/src/main/java/de/thm/arsnova/service/DefaultEntityServiceImpl.java b/src/main/java/de/thm/arsnova/service/DefaultEntityServiceImpl.java index e2f2d0272238cc58ce9adad3c6612c20dc755f05..114c0be1039794d74902a8a5d1f07539dbdab92a 100644 --- a/src/main/java/de/thm/arsnova/service/DefaultEntityServiceImpl.java +++ b/src/main/java/de/thm/arsnova/service/DefaultEntityServiceImpl.java @@ -22,12 +22,12 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import de.thm.arsnova.event.AfterCreationEvent; import de.thm.arsnova.event.AfterDeletionEvent; +import de.thm.arsnova.event.AfterFullUpdateEvent; import de.thm.arsnova.event.AfterPatchEvent; -import de.thm.arsnova.event.AfterUpdateEvent; import de.thm.arsnova.event.BeforeCreationEvent; import de.thm.arsnova.event.BeforeDeletionEvent; +import de.thm.arsnova.event.BeforeFullUpdateEvent; import de.thm.arsnova.event.BeforePatchEvent; -import de.thm.arsnova.event.BeforeUpdateEvent; import de.thm.arsnova.model.Entity; import de.thm.arsnova.model.serialization.View; import de.thm.arsnova.persistence.CrudRepository; @@ -130,9 +130,9 @@ public class DefaultEntityServiceImpl<T extends Entity> implements EntityService newEntity.setUpdateTimestamp(new Date()); prepareUpdate(newEntity); - eventPublisher.publishEvent(new BeforeUpdateEvent<>(this, newEntity)); + eventPublisher.publishEvent(new BeforeFullUpdateEvent<>(this, newEntity, oldEntity)); final T updatedEntity = repository.save(newEntity); - eventPublisher.publishEvent(new AfterUpdateEvent<>(this, updatedEntity)); + eventPublisher.publishEvent(new AfterFullUpdateEvent<>(this, updatedEntity, oldEntity)); finalizeUpdate(updatedEntity); return updatedEntity; @@ -171,9 +171,9 @@ public class DefaultEntityServiceImpl<T extends Entity> implements EntityService reader.readValue(tree); entity.setUpdateTimestamp(new Date()); preparePatch(entity); - eventPublisher.publishEvent(new BeforePatchEvent<>(this, entity)); + eventPublisher.publishEvent(new BeforePatchEvent<>(this, entity, changes)); final T patchedEntity = repository.save(entity); - eventPublisher.publishEvent(new AfterPatchEvent<>(this, patchedEntity)); + eventPublisher.publishEvent(new AfterPatchEvent<>(this, patchedEntity, changes)); return patchedEntity; } @@ -194,7 +194,7 @@ public class DefaultEntityServiceImpl<T extends Entity> implements EntityService reader.readValue(tree); entity.setUpdateTimestamp(new Date()); preparePatch(entity); - eventPublisher.publishEvent(new BeforePatchEvent<>(this, entity)); + eventPublisher.publishEvent(new BeforePatchEvent<>(this, entity, changes)); } return repository.saveAll(entities);