diff --git a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java b/src/main/java/de/thm/arsnova/dao/CouchDBDao.java index 326dbec1395dcc7faa4346454982cae4b45732f9..778361535cc4dcc3217800574d6dbe95c33e9768 100644 --- a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java +++ b/src/main/java/de/thm/arsnova/dao/CouchDBDao.java @@ -45,6 +45,8 @@ import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; 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.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Isolation; @@ -68,13 +70,15 @@ import de.thm.arsnova.entities.Session; import de.thm.arsnova.entities.SessionInfo; import de.thm.arsnova.entities.User; import de.thm.arsnova.entities.VisitedSession; +import de.thm.arsnova.entities.transport.AnswerQueueElement; import de.thm.arsnova.entities.transport.ImportExportSession; import de.thm.arsnova.entities.transport.ImportExportSession.ImportExportQuestion; +import de.thm.arsnova.events.NewAnswerEvent; import de.thm.arsnova.exceptions.NotFoundException; import de.thm.arsnova.services.ISessionService; @Component("databaseDao") -public class CouchDBDao implements IDatabaseDao { +public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware { @Autowired private ISessionService sessionService; @@ -84,7 +88,9 @@ public class CouchDBDao implements IDatabaseDao { private String databaseName; private Database database; - private final Queue<AbstractMap.SimpleEntry<Document, Answer>> answerQueue = new ConcurrentLinkedQueue<AbstractMap.SimpleEntry<Document, Answer>>(); + private ApplicationEventPublisher publisher; + + private final Queue<AbstractMap.SimpleEntry<Document, AnswerQueueElement>> answerQueue = new ConcurrentLinkedQueue<AbstractMap.SimpleEntry<Document, AnswerQueueElement>>(); public static final Logger LOGGER = LoggerFactory.getLogger(CouchDBDao.class); @@ -107,6 +113,11 @@ public class CouchDBDao implements IDatabaseDao { sessionService = service; } + @Override + public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { + this.publisher = publisher; + } + @Override public List<Session> getMySessions(final User user) { final NovaView view = new NovaView("session/by_creator"); @@ -1212,9 +1223,8 @@ public class CouchDBDao implements IDatabaseDao { return this.getInfosForVisitedSessions(sessions, user); } - @CacheEvict(value = "answers", allEntries = true) @Override - public Answer saveAnswer(final Answer answer, final User user) { + public Answer saveAnswer(final Answer answer, final User user, final Question question, final Session session) { final Document a = new Document(); a.put("type", "skill_question_answer"); a.put("sessionId", answer.getSessionId()); @@ -1227,20 +1237,25 @@ public class CouchDBDao implements IDatabaseDao { a.put("user", user.getUsername()); a.put("piRound", answer.getPiRound()); a.put("abstention", answer.isAbstention()); - this.answerQueue.offer(new AbstractMap.SimpleEntry<Document, Answer>(a, answer)); + AnswerQueueElement answerQueueElement = new AnswerQueueElement(session, question, answer, user); + this.answerQueue.offer(new AbstractMap.SimpleEntry<Document, AnswerQueueElement>(a, answerQueueElement)); return answer; } + @Caching(evict = { @CacheEvict(value = "answers", allEntries = true), + @CacheEvict(value = "learningprogress", allEntries = true)}) @Scheduled(fixedDelay = 5000) public void flushAnswerQueue() { final Map<Document, Answer> map = new HashMap<Document, Answer>(); final List<Document> answerList = new ArrayList<Document>(); - AbstractMap.SimpleEntry<Document, Answer> entry; + final List<AnswerQueueElement> elements = new ArrayList<AnswerQueueElement>(); + AbstractMap.SimpleEntry<Document, AnswerQueueElement> entry; while ((entry = this.answerQueue.poll()) != null) { final Document doc = entry.getKey(); - final Answer answer = entry.getValue(); + final Answer answer = entry.getValue().getAnswer(); map.put(doc, answer); answerList.add(doc); + elements.add(entry.getValue()); } if (answerList.isEmpty()) { // no need to send an empty bulk request. ;-) @@ -1253,6 +1268,10 @@ public class CouchDBDao implements IDatabaseDao { answer.set_id(d.getId()); answer.set_rev(d.getRev()); } + // Send NewAnswerEvents ... + for (AnswerQueueElement e : elements) { + this.publisher.publishEvent(new NewAnswerEvent(this, e.getSession(), e.getAnswer(), e.getUser(), e.getQuestion())); + } } catch (IOException e) { LOGGER.error("Could not bulk save answers from queue"); } diff --git a/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java b/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java index 6d0fd41c815f404bf7c97eabb212c62053601a4c..217bbed2ba194a1961ab9e280aa0a472e037fe87 100644 --- a/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java +++ b/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java @@ -124,7 +124,7 @@ public interface IDatabaseDao { void deleteAnswers(Question question); - Answer saveAnswer(Answer answer, User user); + Answer saveAnswer(Answer answer, User user, Question question, Session session); Answer updateAnswer(Answer answer); diff --git a/src/main/java/de/thm/arsnova/entities/transport/AnswerQueueElement.java b/src/main/java/de/thm/arsnova/entities/transport/AnswerQueueElement.java new file mode 100644 index 0000000000000000000000000000000000000000..a8af5aa35d89855c3f1a2019060214fcce578926 --- /dev/null +++ b/src/main/java/de/thm/arsnova/entities/transport/AnswerQueueElement.java @@ -0,0 +1,57 @@ +/* + * This file is part of ARSnova Backend. + * Copyright (C) 2012-2015 The ARSnova Team + * + * ARSnova Backend is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ARSnova Backend is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package de.thm.arsnova.entities.transport; + +import de.thm.arsnova.entities.Answer; +import de.thm.arsnova.entities.Question; +import de.thm.arsnova.entities.Session; +import de.thm.arsnova.entities.User; + +public class AnswerQueueElement { + + private final Session session; + + private final Question question; + + private final Answer answer; + + private final User user; + + public AnswerQueueElement(Session session, Question question, Answer answer, User user) { + this.session = session; + this.question = question; + this.answer = answer; + this.user = user; + } + + public Session getSession() { + return session; + } + + public Question getQuestion() { + return question; + } + + public Answer getAnswer() { + return answer; + } + + public User getUser() { + return user; + } +} diff --git a/src/main/java/de/thm/arsnova/services/QuestionService.java b/src/main/java/de/thm/arsnova/services/QuestionService.java index 28c02274155d27255bc06188e1ed45cb68fe57f5..9db7d05d5fb508420e6d74f257a954a6e54720c3 100644 --- a/src/main/java/de/thm/arsnova/services/QuestionService.java +++ b/src/main/java/de/thm/arsnova/services/QuestionService.java @@ -463,11 +463,7 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis Answer theAnswer = answer.generateAnswerEntity(user, question); - final Answer result = databaseDao.saveAnswer(theAnswer, user); - final Session session = databaseDao.getSessionFromKeyword(question.getSessionKeyword()); - this.publisher.publishEvent(new NewAnswerEvent(this, session, result, user, question)); - - return result; + return databaseDao.saveAnswer(theAnswer, user, question, getSession(question.getSessionKeyword())); } @Override