From c48e865a6584a533477aa7dbe350e9cf21b31c18 Mon Sep 17 00:00:00 2001
From: Christoph Thelen <christoph.thelen@mni.thm.de>
Date: Wed, 12 Nov 2014 17:15:42 +0100
Subject: [PATCH] Cleaner use of interposed entity with additional web socket
 support

---
 .../AudienceQuestionController.java           | 13 ++--
 .../java/de/thm/arsnova/dao/CouchDBDao.java   |  6 +-
 .../transport/InterposedQuestion.java         | 71 +++++++++++++++++++
 .../events/DeleteInterposedQuestionEvent.java | 26 +++++++
 .../arsnova/services/IQuestionService.java    |  2 +
 .../thm/arsnova/services/QuestionService.java | 16 ++++-
 .../arsnova/socket/ARSnovaSocketIOServer.java | 14 ++++
 7 files changed, 135 insertions(+), 13 deletions(-)
 create mode 100644 src/main/java/de/thm/arsnova/entities/transport/InterposedQuestion.java
 create mode 100644 src/main/java/de/thm/arsnova/events/DeleteInterposedQuestionEvent.java

diff --git a/src/main/java/de/thm/arsnova/controller/AudienceQuestionController.java b/src/main/java/de/thm/arsnova/controller/AudienceQuestionController.java
index 3f42d627e..a674b6b24 100644
--- a/src/main/java/de/thm/arsnova/controller/AudienceQuestionController.java
+++ b/src/main/java/de/thm/arsnova/controller/AudienceQuestionController.java
@@ -32,10 +32,9 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseStatus;
 import org.springframework.web.bind.annotation.RestController;
 
-import de.thm.arsnova.entities.InterposedQuestion;
+import de.thm.arsnova.entities.transport.InterposedQuestion;
 import de.thm.arsnova.entities.InterposedReadingCount;
 import de.thm.arsnova.exceptions.BadRequestException;
-import de.thm.arsnova.exceptions.PreconditionFailedException;
 import de.thm.arsnova.services.IQuestionService;
 import de.thm.arsnova.web.DeprecatedApi;
 
@@ -62,24 +61,20 @@ public class AudienceQuestionController extends AbstractController {
 
 	@RequestMapping(value = "/", method = RequestMethod.GET)
 	public final List<InterposedQuestion> getInterposedQuestions(@RequestParam final String sessionkey) {
-		return questionService.getInterposedQuestions(sessionkey);
+		return InterposedQuestion.fromList(questionService.getInterposedQuestions(sessionkey));
 	}
 
 	@RequestMapping(value = "/{questionId}", method = RequestMethod.GET)
 	public final InterposedQuestion getInterposedQuestion(@PathVariable final String questionId) {
-		return questionService.readInterposedQuestion(questionId);
+		return new InterposedQuestion(questionService.readInterposedQuestion(questionId));
 	}
 
 	@RequestMapping(value = "/", method = RequestMethod.POST)
 	@ResponseStatus(HttpStatus.CREATED)
 	public final void postInterposedQuestion(
 			@RequestParam final String sessionkey,
-			@RequestBody final InterposedQuestion question
+			@RequestBody final de.thm.arsnova.entities.InterposedQuestion question
 			) {
-		if (!sessionkey.equals(question.getSessionId())) {
-			throw new PreconditionFailedException();
-		}
-
 		if (questionService.saveQuestion(question)) {
 			return;
 		}
diff --git a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java b/src/main/java/de/thm/arsnova/dao/CouchDBDao.java
index cdc03e8cf..2f43cad0b 100644
--- a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java
+++ b/src/main/java/de/thm/arsnova/dao/CouchDBDao.java
@@ -424,7 +424,7 @@ public class CouchDBDao implements IDatabaseDao {
 		q.put("gridType", question.getGridType());
 		q.put("scaleFactor", question.getScaleFactor());
 		q.put("gridScaleFactor", question.getGridScaleFactor());
-		
+
 		return q;
 	}
 
@@ -463,7 +463,7 @@ public class CouchDBDao implements IDatabaseDao {
 			q.put("gridType", question.getGridType());
 			q.put("scaleFactor", question.getScaleFactor());
 			q.put("gridScaleFactor", question.getGridScaleFactor());
-			
+
 			database.saveDocument(q);
 			question.set_rev(q.getRev());
 
@@ -876,7 +876,7 @@ public class CouchDBDao implements IDatabaseDao {
 
 	@Override
 	public List<InterposedQuestion> getInterposedQuestions(final Session session) {
-		final NovaView view = new NovaView("interposed_question/by_session");
+		final NovaView view = new NovaView("interposed_question/by_session_full");
 		view.setKey(session.get_id());
 		final ViewResults questions = getDatabase().view(view);
 		if (questions == null || questions.isEmpty()) {
diff --git a/src/main/java/de/thm/arsnova/entities/transport/InterposedQuestion.java b/src/main/java/de/thm/arsnova/entities/transport/InterposedQuestion.java
new file mode 100644
index 000000000..702836ccc
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/entities/transport/InterposedQuestion.java
@@ -0,0 +1,71 @@
+package de.thm.arsnova.entities.transport;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class InterposedQuestion {
+
+	private String id;
+	private String subject;
+	private String text;
+	private long timestamp;
+	private boolean read;
+
+	public static List<InterposedQuestion> fromList(List<de.thm.arsnova.entities.InterposedQuestion> questions) {
+		ArrayList<InterposedQuestion> interposedQuestions = new ArrayList<InterposedQuestion>();
+		for (de.thm.arsnova.entities.InterposedQuestion question : questions) {
+			interposedQuestions.add(new InterposedQuestion(question));
+		}
+		return interposedQuestions;
+	}
+
+	public InterposedQuestion(de.thm.arsnova.entities.InterposedQuestion question) {
+		this.id = question.get_id();
+		this.subject = question.getSubject();
+		this.text = question.getText();
+		this.timestamp = question.getTimestamp();
+		this.read = question.isRead();
+	}
+
+	public InterposedQuestion() {}
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public String getSubject() {
+		return subject;
+	}
+
+	public void setSubject(String subject) {
+		this.subject = subject;
+	}
+
+	public String getText() {
+		return text;
+	}
+
+	public void setText(String text) {
+		this.text = text;
+	}
+
+	public long getTimestamp() {
+		return timestamp;
+	}
+
+	public void setTimestamp(long timestamp) {
+		this.timestamp = timestamp;
+	}
+
+	public boolean isRead() {
+		return read;
+	}
+
+	public void setRead(boolean read) {
+		this.read = read;
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/events/DeleteInterposedQuestionEvent.java b/src/main/java/de/thm/arsnova/events/DeleteInterposedQuestionEvent.java
new file mode 100644
index 000000000..b8aea746a
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/events/DeleteInterposedQuestionEvent.java
@@ -0,0 +1,26 @@
+package de.thm.arsnova.events;
+
+import de.thm.arsnova.entities.InterposedQuestion;
+import de.thm.arsnova.entities.Session;
+
+public class DeleteInterposedQuestionEvent extends NovaEvent {
+
+	private static final long serialVersionUID = 1L;
+
+	private final Session session;
+
+	private final InterposedQuestion question;
+
+	public DeleteInterposedQuestionEvent(Object source, Session session, InterposedQuestion question) {
+		super(source);
+		this.session = session;
+		this.question = question;
+	}
+
+	@Override
+	public void accept(NovaEventVisitor visitor) {
+		// TODO Auto-generated method stub
+
+	}
+
+}
diff --git a/src/main/java/de/thm/arsnova/services/IQuestionService.java b/src/main/java/de/thm/arsnova/services/IQuestionService.java
index 4b1746d1f..a964c77e3 100644
--- a/src/main/java/de/thm/arsnova/services/IQuestionService.java
+++ b/src/main/java/de/thm/arsnova/services/IQuestionService.java
@@ -66,6 +66,8 @@ public interface IQuestionService {
 
 	InterposedQuestion readInterposedQuestion(String questionId);
 
+	InterposedQuestion readInterposedQuestionInternal(String questionId, User user);
+
 	Question update(Question question);
 
 	void deleteAnswers(String questionId);
diff --git a/src/main/java/de/thm/arsnova/services/QuestionService.java b/src/main/java/de/thm/arsnova/services/QuestionService.java
index 0607ac273..2224949f8 100644
--- a/src/main/java/de/thm/arsnova/services/QuestionService.java
+++ b/src/main/java/de/thm/arsnova/services/QuestionService.java
@@ -42,6 +42,7 @@ import de.thm.arsnova.entities.Question;
 import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.User;
 import de.thm.arsnova.events.DeleteAnswerEvent;
+import de.thm.arsnova.events.DeleteInterposedQuestionEvent;
 import de.thm.arsnova.events.NewAnswerEvent;
 import de.thm.arsnova.events.NewInterposedQuestionEvent;
 import de.thm.arsnova.events.NewQuestionEvent;
@@ -191,6 +192,10 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 			throw new NotFoundException();
 		}
 		databaseDao.deleteInterposedQuestion(question);
+
+		final Session session = databaseDao.getSessionFromKeyword(question.getSessionId());
+		final DeleteInterposedQuestionEvent event = new DeleteInterposedQuestionEvent(this, session, question);
+		this.publisher.publishEvent(event);
 	}
 
 	@Override
@@ -358,13 +363,22 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public InterposedQuestion readInterposedQuestion(final String questionId) {
+		final User user = userService.getCurrentUser();
+		return this.readInterposedQuestionInternal(questionId, user);
+	}
+
+	/*
+	 * The "internal" suffix means it is called by internal services that have no authentication!
+	 * TODO: Find a better way of doing this...
+	 */
+	@Override
+	public InterposedQuestion readInterposedQuestionInternal(final String questionId, User user) {
 		final InterposedQuestion question = databaseDao.getInterposedQuestion(questionId);
 		if (question == null) {
 			throw new NotFoundException();
 		}
 		final Session session = databaseDao.getSessionFromKeyword(question.getSessionId());
 
-		final User user = userService.getCurrentUser();
 		if (session.isCreator(user)) {
 			databaseDao.markInterposedQuestionAsRead(question);
 		}
diff --git a/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java b/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java
index 254ddbc9a..4f20db3ed 100644
--- a/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java
+++ b/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java
@@ -151,6 +151,20 @@ public class ARSnovaSocketIOServer implements ApplicationListener<NovaEvent>, No
 			}
 		});
 
+		server.addEventListener(
+				"readInterposedQuestion",
+				de.thm.arsnova.entities.transport.InterposedQuestion.class,
+				new DataListener<de.thm.arsnova.entities.transport.InterposedQuestion>() {
+			@Override
+			public void onData(
+					SocketIOClient client,
+					de.thm.arsnova.entities.transport.InterposedQuestion question,
+					AckRequest ackRequest) {
+				final User user = userService.getUser2SocketId(client.getSessionId());
+				questionService.readInterposedQuestionInternal(question.getId(), user);
+			}
+		});
+
 		server.addConnectListener(new ConnectListener() {
 			@Override
 			public void onConnect(final SocketIOClient client) { }
-- 
GitLab