From 125bbf9005474f8a2dfd0397e23b4264ef5c79ca Mon Sep 17 00:00:00 2001
From: Daniel Gerhardt <code@dgerhardt.net>
Date: Mon, 5 Nov 2018 14:14:24 +0100
Subject: [PATCH] Emit events before and after entity CRUD operations

---
 .../thm/arsnova/event/AfterCreationEvent.java |  9 ++++++
 .../thm/arsnova/event/AfterDeletionEvent.java |  9 ++++++
 .../de/thm/arsnova/event/AfterPatchEvent.java |  9 ++++++
 .../thm/arsnova/event/AfterUpdateEvent.java   |  9 ++++++
 .../arsnova/event/BeforeCreationEvent.java    |  9 ++++++
 .../arsnova/event/BeforeDeletionEvent.java    |  9 ++++++
 .../thm/arsnova/event/BeforePatchEvent.java   |  9 ++++++
 .../thm/arsnova/event/BeforeUpdateEvent.java  |  9 ++++++
 .../java/de/thm/arsnova/event/CrudEvent.java  | 15 ++++++++++
 .../arsnova/service/AnswerServiceImpl.java    | 18 +++---------
 .../arsnova/service/CommentServiceImpl.java   | 13 ++-------
 .../arsnova/service/ContentServiceImpl.java   | 29 +++++++------------
 .../service/DefaultEntityServiceImpl.java     | 29 +++++++++++++++++--
 .../thm/arsnova/service/RoomServiceImpl.java  | 21 ++++----------
 .../service/DefaultEntityServiceImplTest.java |  6 ++++
 15 files changed, 142 insertions(+), 61 deletions(-)
 create mode 100644 src/main/java/de/thm/arsnova/event/AfterCreationEvent.java
 create mode 100644 src/main/java/de/thm/arsnova/event/AfterDeletionEvent.java
 create mode 100644 src/main/java/de/thm/arsnova/event/AfterPatchEvent.java
 create mode 100644 src/main/java/de/thm/arsnova/event/AfterUpdateEvent.java
 create mode 100644 src/main/java/de/thm/arsnova/event/BeforeCreationEvent.java
 create mode 100644 src/main/java/de/thm/arsnova/event/BeforeDeletionEvent.java
 create mode 100644 src/main/java/de/thm/arsnova/event/BeforePatchEvent.java
 create mode 100644 src/main/java/de/thm/arsnova/event/BeforeUpdateEvent.java
 create mode 100644 src/main/java/de/thm/arsnova/event/CrudEvent.java

diff --git a/src/main/java/de/thm/arsnova/event/AfterCreationEvent.java b/src/main/java/de/thm/arsnova/event/AfterCreationEvent.java
new file mode 100644
index 000000000..6f99fd855
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/event/AfterCreationEvent.java
@@ -0,0 +1,9 @@
+package de.thm.arsnova.event;
+
+import de.thm.arsnova.model.Entity;
+
+public class AfterCreationEvent<E extends Entity> extends CrudEvent<E> {
+	public AfterCreationEvent(final E source) {
+		super(source);
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/event/AfterDeletionEvent.java b/src/main/java/de/thm/arsnova/event/AfterDeletionEvent.java
new file mode 100644
index 000000000..67893207a
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/event/AfterDeletionEvent.java
@@ -0,0 +1,9 @@
+package de.thm.arsnova.event;
+
+import de.thm.arsnova.model.Entity;
+
+public class AfterDeletionEvent<E extends Entity> extends CrudEvent<E> {
+	public AfterDeletionEvent(final E source) {
+		super(source);
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/event/AfterPatchEvent.java b/src/main/java/de/thm/arsnova/event/AfterPatchEvent.java
new file mode 100644
index 000000000..263821074
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/event/AfterPatchEvent.java
@@ -0,0 +1,9 @@
+package de.thm.arsnova.event;
+
+import de.thm.arsnova.model.Entity;
+
+public class AfterPatchEvent<E extends Entity> extends AfterUpdateEvent<E> {
+	public AfterPatchEvent(final E source) {
+		super(source);
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/event/AfterUpdateEvent.java b/src/main/java/de/thm/arsnova/event/AfterUpdateEvent.java
new file mode 100644
index 000000000..b30f3958a
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/event/AfterUpdateEvent.java
@@ -0,0 +1,9 @@
+package de.thm.arsnova.event;
+
+import de.thm.arsnova.model.Entity;
+
+public class AfterUpdateEvent<E extends Entity> extends CrudEvent<E> {
+	public AfterUpdateEvent(final E source) {
+		super(source);
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/event/BeforeCreationEvent.java b/src/main/java/de/thm/arsnova/event/BeforeCreationEvent.java
new file mode 100644
index 000000000..42d5524f3
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/event/BeforeCreationEvent.java
@@ -0,0 +1,9 @@
+package de.thm.arsnova.event;
+
+import de.thm.arsnova.model.Entity;
+
+public class BeforeCreationEvent<E extends Entity> extends CrudEvent<E> {
+	public BeforeCreationEvent(final E source) {
+		super(source);
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/event/BeforeDeletionEvent.java b/src/main/java/de/thm/arsnova/event/BeforeDeletionEvent.java
new file mode 100644
index 000000000..0a13bef26
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/event/BeforeDeletionEvent.java
@@ -0,0 +1,9 @@
+package de.thm.arsnova.event;
+
+import de.thm.arsnova.model.Entity;
+
+public class BeforeDeletionEvent<E extends Entity> extends CrudEvent<E> {
+	public BeforeDeletionEvent(final E source) {
+		super(source);
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/event/BeforePatchEvent.java b/src/main/java/de/thm/arsnova/event/BeforePatchEvent.java
new file mode 100644
index 000000000..871dc6bfd
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/event/BeforePatchEvent.java
@@ -0,0 +1,9 @@
+package de.thm.arsnova.event;
+
+import de.thm.arsnova.model.Entity;
+
+public class BeforePatchEvent<E extends Entity> extends BeforeUpdateEvent<E> {
+	public BeforePatchEvent(final E source) {
+		super(source);
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/event/BeforeUpdateEvent.java b/src/main/java/de/thm/arsnova/event/BeforeUpdateEvent.java
new file mode 100644
index 000000000..81c74e675
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/event/BeforeUpdateEvent.java
@@ -0,0 +1,9 @@
+package de.thm.arsnova.event;
+
+import de.thm.arsnova.model.Entity;
+
+public class BeforeUpdateEvent<E extends Entity> extends CrudEvent<E> {
+	public BeforeUpdateEvent(final E source) {
+		super(source);
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/event/CrudEvent.java b/src/main/java/de/thm/arsnova/event/CrudEvent.java
new file mode 100644
index 000000000..bf41ce6ff
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/event/CrudEvent.java
@@ -0,0 +1,15 @@
+package de.thm.arsnova.event;
+
+import de.thm.arsnova.model.Entity;
+import org.springframework.context.ApplicationEvent;
+
+public abstract class CrudEvent<E extends Entity> extends ApplicationEvent {
+	public CrudEvent(final E source) {
+		super(source);
+	}
+
+	@Override
+	public E getSource() {
+		return (E) super.getSource();
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/service/AnswerServiceImpl.java b/src/main/java/de/thm/arsnova/service/AnswerServiceImpl.java
index c7dd6e561..93e7c1bfd 100644
--- a/src/main/java/de/thm/arsnova/service/AnswerServiceImpl.java
+++ b/src/main/java/de/thm/arsnova/service/AnswerServiceImpl.java
@@ -37,8 +37,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.cache.annotation.CacheEvict;
-import org.springframework.context.ApplicationEventPublisher;
-import org.springframework.context.ApplicationEventPublisherAware;
 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -55,14 +53,11 @@ import java.util.concurrent.ConcurrentLinkedQueue;
  * Performs all answer related operations.
  */
 @Service
-public class AnswerServiceImpl extends DefaultEntityServiceImpl<Answer>
-		implements AnswerService, ApplicationEventPublisherAware {
+public class AnswerServiceImpl extends DefaultEntityServiceImpl<Answer> implements AnswerService {
 	private static final Logger logger = LoggerFactory.getLogger(ContentServiceImpl.class);
 
 	private final Queue<AnswerQueueElement> answerQueue = new ConcurrentLinkedQueue<>();
 
-	private ApplicationEventPublisher publisher;
-
 	private RoomRepository roomRepository;
 	private ContentRepository contentRepository;
 	private AnswerRepository answerRepository;
@@ -104,7 +99,7 @@ public class AnswerServiceImpl extends DefaultEntityServiceImpl<Answer>
 
 			// Send NewAnswerEvents ...
 			for (AnswerQueueElement e : elements) {
-				this.publisher.publishEvent(new NewAnswerEvent(this, e.getRoom(), e.getAnswer(), e.getUserId(), e.getQuestion()));
+				this.eventPublisher.publishEvent(new NewAnswerEvent(this, e.getRoom(), e.getAnswer(), e.getUserId(), e.getQuestion()));
 			}
 		} catch (final DbAccessException e) {
 			logger.error("Could not bulk save answers from queue.", e);
@@ -379,7 +374,7 @@ public class AnswerServiceImpl extends DefaultEntityServiceImpl<Answer>
 		answer.setContentId(content.getId());
 		answer.setRoomId(room.getId());
 		answerRepository.save(realAnswer);
-		this.publisher.publishEvent(new NewAnswerEvent(this, room, answer, user.getId(), content));
+		this.eventPublisher.publishEvent(new NewAnswerEvent(this, room, answer, user.getId(), content));
 
 		return answer;
 	}
@@ -399,7 +394,7 @@ public class AnswerServiceImpl extends DefaultEntityServiceImpl<Answer>
 		}
 		answerRepository.deleteById(answerId);
 
-		this.publisher.publishEvent(new DeleteAnswerEvent(this, room, content));
+		this.eventPublisher.publishEvent(new DeleteAnswerEvent(this, room, content));
 	}
 
 	/*
@@ -447,9 +442,4 @@ public class AnswerServiceImpl extends DefaultEntityServiceImpl<Answer>
 	public int countPreparationQuestionAnswersInternal(final String roomId) {
 		return answerRepository.countByRoomIdOnlyPreparationVariant(roomRepository.findOne(roomId).getId());
 	}
-
-	@Override
-	public void setApplicationEventPublisher(final ApplicationEventPublisher applicationEventPublisher) {
-		this.publisher = applicationEventPublisher;
-	}
 }
diff --git a/src/main/java/de/thm/arsnova/service/CommentServiceImpl.java b/src/main/java/de/thm/arsnova/service/CommentServiceImpl.java
index 725b1bc35..8fbab006c 100644
--- a/src/main/java/de/thm/arsnova/service/CommentServiceImpl.java
+++ b/src/main/java/de/thm/arsnova/service/CommentServiceImpl.java
@@ -11,8 +11,6 @@ import de.thm.arsnova.web.exceptions.ForbiddenException;
 import de.thm.arsnova.web.exceptions.NotFoundException;
 import de.thm.arsnova.web.exceptions.UnauthorizedException;
 import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.context.ApplicationEventPublisher;
-import org.springframework.context.ApplicationEventPublisherAware;
 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Service;
@@ -27,15 +25,13 @@ import java.util.Map;
  * Performs all comment related operations.
  */
 @Service
-public class CommentServiceImpl extends DefaultEntityServiceImpl<Comment> implements CommentService, ApplicationEventPublisherAware {
+public class CommentServiceImpl extends DefaultEntityServiceImpl<Comment> implements CommentService {
 	private UserService userService;
 
 	private CommentRepository commentRepository;
 
 	private RoomRepository roomRepository;
 
-	private ApplicationEventPublisher publisher;
-
 	public CommentServiceImpl(
 			CommentRepository repository,
 			RoomRepository roomRepository,
@@ -47,11 +43,6 @@ public class CommentServiceImpl extends DefaultEntityServiceImpl<Comment> implem
 		this.userService = userService;
 	}
 
-	@Override
-	public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
-		this.publisher = applicationEventPublisher;
-	}
-
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public void prepareCreate(final Comment comment) {
@@ -76,7 +67,7 @@ public class CommentServiceImpl extends DefaultEntityServiceImpl<Comment> implem
 
 		final Room room = roomRepository.findOne(comment.getRoomId());
 		final DeleteCommentEvent event = new DeleteCommentEvent(this, room, comment);
-		this.publisher.publishEvent(event);
+		this.eventPublisher.publishEvent(event);
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/service/ContentServiceImpl.java b/src/main/java/de/thm/arsnova/service/ContentServiceImpl.java
index 1b0aee135..ea65cbe35 100644
--- a/src/main/java/de/thm/arsnova/service/ContentServiceImpl.java
+++ b/src/main/java/de/thm/arsnova/service/ContentServiceImpl.java
@@ -34,8 +34,6 @@ import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.cache.annotation.Caching;
-import org.springframework.context.ApplicationEventPublisher;
-import org.springframework.context.ApplicationEventPublisherAware;
 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Service;
@@ -54,7 +52,7 @@ import java.util.stream.Collectors;
  * Performs all content related operations.
  */
 @Service
-public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implements ContentService, ApplicationEventPublisherAware {
+public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implements ContentService {
 	private UserService userService;
 
 	private LogEntryRepository dbLogger;
@@ -65,8 +63,6 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 
 	private AnswerRepository answerRepository;
 
-	private ApplicationEventPublisher publisher;
-
 	private static final Logger logger = LoggerFactory.getLogger(ContentServiceImpl.class);
 
 	public ContentServiceImpl(
@@ -201,7 +197,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		roomRepository.save(room);
 
 		final NewQuestionEvent event = new NewQuestionEvent(this, room, content);
-		this.publisher.publishEvent(event);
+		this.eventPublisher.publishEvent(event);
 	}
 
 	@Override
@@ -301,7 +297,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		}
 
 		final DeleteQuestionEvent event = new DeleteQuestionEvent(this, room, content);
-		this.publisher.publishEvent(event);
+		this.eventPublisher.publishEvent(event);
 	}
 
 	@PreAuthorize("hasPermission(#session, 'owner')")
@@ -325,7 +321,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		dbLogger.log("delete", "type", "answer", "answerCount", answerCount);
 
 		final DeleteAllQuestionsEvent event = new DeleteAllQuestionsEvent(this, room);
-		this.publisher.publishEvent(event);
+		this.eventPublisher.publishEvent(event);
 	}
 
 	@Override
@@ -375,7 +371,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		} else {
 			event = new UnlockVoteEvent(this, room, content);
 		}
-		this.publisher.publishEvent(event);
+		this.eventPublisher.publishEvent(event);
 	}
 
 	@Override
@@ -405,7 +401,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 			} else {
 				event = new UnlockVotesEvent(this, room, list);
 			}
-			this.publisher.publishEvent(event);
+			this.eventPublisher.publishEvent(event);
 		} catch (IOException e) {
 			logger.error("Patching of contents failed", e);
 		}
@@ -506,7 +502,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		} else {
 			event = new LockQuestionsEvent(this, room, list);
 		}
-		this.publisher.publishEvent(event);
+		this.eventPublisher.publishEvent(event);
 	}
 
 	/* TODO: Split and move answer part to AnswerService */
@@ -525,7 +521,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		final List<String> contentIds = contents.stream().map(Content::getId).collect(Collectors.toList());
 		answerRepository.deleteAllAnswersForQuestions(contentIds);
 
-		this.publisher.publishEvent(new DeleteAllQuestionsAnswersEvent(this, room));
+		this.eventPublisher.publishEvent(new DeleteAllQuestionsAnswersEvent(this, room));
 	}
 
 	/* TODO: Split and move answer part to AnswerService */
@@ -541,7 +537,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		final List<String> contentIds = contents.stream().map(Content::getId).collect(Collectors.toList());
 		answerRepository.deleteAllAnswersForQuestions(contentIds);
 
-		this.publisher.publishEvent(new DeleteAllPreparationAnswersEvent(this, room));
+		this.eventPublisher.publishEvent(new DeleteAllPreparationAnswersEvent(this, room));
 	}
 
 	/* TODO: Split and move answer part to AnswerService */
@@ -557,7 +553,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		final List<String> contentIds = contents.stream().map(Content::getId).collect(Collectors.toList());
 		answerRepository.deleteAllAnswersForQuestions(contentIds);
 
-		this.publisher.publishEvent(new DeleteAllLectureAnswersEvent(this, room));
+		this.eventPublisher.publishEvent(new DeleteAllLectureAnswersEvent(this, room));
 	}
 
 	@Caching(evict = {
@@ -574,9 +570,4 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		}
 		contentRepository.saveAll(contents);
 	}
-
-	@Override
-	public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
-		this.publisher = publisher;
-	}
 }
diff --git a/src/main/java/de/thm/arsnova/service/DefaultEntityServiceImpl.java b/src/main/java/de/thm/arsnova/service/DefaultEntityServiceImpl.java
index 9f11f884b..3e1ab7746 100644
--- a/src/main/java/de/thm/arsnova/service/DefaultEntityServiceImpl.java
+++ b/src/main/java/de/thm/arsnova/service/DefaultEntityServiceImpl.java
@@ -20,9 +20,18 @@ package de.thm.arsnova.service;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.ObjectReader;
+import de.thm.arsnova.event.AfterCreationEvent;
+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.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;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.ApplicationEventPublisherAware;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.security.access.prepost.PreFilter;
 
@@ -38,9 +47,10 @@ import java.util.function.Function;
  * @param <T> Entity type
  * @author Daniel Gerhardt
  */
-public class DefaultEntityServiceImpl<T extends Entity> implements EntityService<T> {
+public class DefaultEntityServiceImpl<T extends Entity> implements EntityService<T>, ApplicationEventPublisherAware {
 	protected Class<T> type;
 	protected CrudRepository<T, String> repository;
+	protected ApplicationEventPublisher eventPublisher;
 	private ObjectMapper objectMapper;
 
 	public DefaultEntityServiceImpl(Class<T> type, CrudRepository<T, String> repository, ObjectMapper objectMapper) {
@@ -82,7 +92,9 @@ public class DefaultEntityServiceImpl<T extends Entity> implements EntityService
 		entity.setCreationTimestamp(new Date());
 
 		prepareCreate(entity);
+		eventPublisher.publishEvent(new BeforeCreationEvent<>(entity));
 		final T createdEntity = repository.save(entity);
+		eventPublisher.publishEvent(new AfterCreationEvent<>(createdEntity));
 		finalizeCreate(entity);
 
 		return createdEntity;
@@ -117,7 +129,9 @@ public class DefaultEntityServiceImpl<T extends Entity> implements EntityService
 		newEntity.setUpdateTimestamp(new Date());
 
 		prepareUpdate(newEntity);
+		eventPublisher.publishEvent(new BeforeUpdateEvent<>(newEntity));
 		final T updatedEntity = repository.save(newEntity);
+		eventPublisher.publishEvent(new AfterUpdateEvent<>(updatedEntity));
 		finalizeUpdate(updatedEntity);
 
 		return updatedEntity;
@@ -156,8 +170,11 @@ public class DefaultEntityServiceImpl<T extends Entity> implements EntityService
 		reader.readValue(tree);
 		entity.setUpdateTimestamp(new Date());
 		preparePatch(entity);
+		eventPublisher.publishEvent(new BeforePatchEvent<>(entity));
+		final T patchedEntity = repository.save(entity);
+		eventPublisher.publishEvent(new AfterPatchEvent<>(entity));
 
-		return repository.save(entity);
+		return patchedEntity;
 	}
 
 	@Override
@@ -176,6 +193,7 @@ public class DefaultEntityServiceImpl<T extends Entity> implements EntityService
 			reader.readValue(tree);
 			entity.setUpdateTimestamp(new Date());
 			preparePatch(entity);
+			eventPublisher.publishEvent(new BeforePatchEvent<>(entity));
 		}
 
 		return repository.saveAll(entities);
@@ -194,7 +212,9 @@ public class DefaultEntityServiceImpl<T extends Entity> implements EntityService
 	@Override
 	@PreAuthorize("hasPermission(#entity, 'delete')")
 	public void delete(final T entity) {
+		eventPublisher.publishEvent(new BeforeDeletionEvent<>(entity));
 		repository.delete(entity);
+		eventPublisher.publishEvent(new AfterUpdateEvent<>(entity));
 	}
 
 	/**
@@ -209,4 +229,9 @@ public class DefaultEntityServiceImpl<T extends Entity> implements EntityService
 	public String getTypeName() {
 		return type.getSimpleName().toLowerCase();
 	}
+
+	@Override
+	public void setApplicationEventPublisher(final ApplicationEventPublisher applicationEventPublisher) {
+		this.eventPublisher = applicationEventPublisher;
+	}
 }
diff --git a/src/main/java/de/thm/arsnova/service/RoomServiceImpl.java b/src/main/java/de/thm/arsnova/service/RoomServiceImpl.java
index f5d566f3f..4f6e2f96e 100644
--- a/src/main/java/de/thm/arsnova/service/RoomServiceImpl.java
+++ b/src/main/java/de/thm/arsnova/service/RoomServiceImpl.java
@@ -47,8 +47,6 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.cache.annotation.Caching;
-import org.springframework.context.ApplicationEventPublisher;
-import org.springframework.context.ApplicationEventPublisherAware;
 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -67,7 +65,7 @@ import java.util.stream.Collectors;
  * Performs all room related operations.
  */
 @Service
-public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements RoomService, ApplicationEventPublisherAware {
+public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements RoomService {
 	private static final long ROOM_INACTIVITY_CHECK_INTERVAL_MS = 30 * 60 * 1000L;
 
 	private static final Logger logger = LoggerFactory.getLogger(RoomServiceImpl.class);
@@ -96,8 +94,6 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
 	@Value("${pp.logofilesize_b}")
 	private int uploadFileSizeByte;
 
-	private ApplicationEventPublisher publisher;
-
 	public RoomServiceImpl(
 			RoomRepository repository,
 			ContentRepository contentRepository,
@@ -407,7 +403,7 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
 	public Room setActive(final String id, final Boolean lock) {
 		final Room room = roomRepository.findOne(id);
 		room.setClosed(!lock);
-		this.publisher.publishEvent(new StatusRoomEvent(this, room));
+		this.eventPublisher.publishEvent(new StatusRoomEvent(this, room));
 		roomRepository.save(room);
 
 		return room;
@@ -462,7 +458,7 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
 		logger.debug("Deleted room document {} and related data.", room.getId());
 		dbLogger.log("delete", "type", "session", "id", room.getId());
 
-		this.publisher.publishEvent(new DeleteRoomEvent(this, room));
+		this.eventPublisher.publishEvent(new DeleteRoomEvent(this, room));
 
 		return count;
 	}
@@ -511,11 +507,6 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
 		return roomRepository.importRoom(user.getId(), temp);
 	}
 
-	@Override
-	public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
-		this.publisher = publisher;
-	}
-
 	@Override
 	@PreAuthorize("hasPermission(#id, 'room', 'read')")
 	public Room.Settings getFeatures(String id) {
@@ -527,7 +518,7 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
 	public Room.Settings updateFeatures(String id, Room.Settings settings) {
 		final Room room = roomRepository.findOne(id);
 		room.setSettings(settings);
-		this.publisher.publishEvent(new FeatureChangeEvent(this, room));
+		this.eventPublisher.publishEvent(new FeatureChangeEvent(this, room));
 		roomRepository.save(room);
 
 		return room.getSettings();
@@ -542,7 +533,7 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
 		}
 
 		room.getSettings().setFeedbackLocked(lock);
-		this.publisher.publishEvent(new LockFeedbackEvent(this, room));
+		this.eventPublisher.publishEvent(new LockFeedbackEvent(this, room));
 		roomRepository.save(room);
 
 		return room.getSettings().isFeedbackLocked();
@@ -552,7 +543,7 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
 	@PreAuthorize("hasPermission(#id, 'room', 'owner')")
 	public boolean flipFlashcards(String id, Boolean flip) {
 		final Room room = roomRepository.findOne(id);
-		this.publisher.publishEvent(new FlipFlashcardsEvent(this, room));
+		this.eventPublisher.publishEvent(new FlipFlashcardsEvent(this, room));
 
 		return flip;
 	}
diff --git a/src/test/java/de/thm/arsnova/service/DefaultEntityServiceImplTest.java b/src/test/java/de/thm/arsnova/service/DefaultEntityServiceImplTest.java
index 7af352356..90cd9d197 100644
--- a/src/test/java/de/thm/arsnova/service/DefaultEntityServiceImplTest.java
+++ b/src/test/java/de/thm/arsnova/service/DefaultEntityServiceImplTest.java
@@ -12,6 +12,7 @@ import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
 import org.springframework.test.context.ActiveProfiles;
 import org.springframework.test.context.ContextConfiguration;
@@ -39,6 +40,9 @@ public class DefaultEntityServiceImplTest {
 	@Qualifier("defaultJsonMessageConverter")
 	private MappingJackson2HttpMessageConverter jackson2HttpMessageConverter;
 
+	@Autowired
+	private ApplicationEventPublisher eventPublisher;
+
 	@Autowired
 	private RoomRepository roomRepository;
 
@@ -47,6 +51,7 @@ public class DefaultEntityServiceImplTest {
 	public void testPatch() throws IOException {
 		final ObjectMapper objectMapper = jackson2HttpMessageConverter.getObjectMapper();
 		final DefaultEntityServiceImpl<Room> entityService = new DefaultEntityServiceImpl<>(Room.class, roomRepository, objectMapper);
+		entityService.setApplicationEventPublisher(eventPublisher);
 
 		when(roomRepository.save(any(Room.class))).then(returnsFirstArg());
 
@@ -80,6 +85,7 @@ public class DefaultEntityServiceImplTest {
 	public void testPatchWithList() throws IOException {
 		final ObjectMapper objectMapper = jackson2HttpMessageConverter.getObjectMapper();
 		final DefaultEntityServiceImpl<Room> entityService = new DefaultEntityServiceImpl<>(Room.class, roomRepository, objectMapper);
+		entityService.setApplicationEventPublisher(eventPublisher);
 
 		when(roomRepository.save(any(Room.class))).then(returnsFirstArg());
 
-- 
GitLab