From 2c9941e42103d7cdfddf9df5c119d488282cacbb Mon Sep 17 00:00:00 2001
From: Daniel Gerhardt <code@dgerhardt.net>
Date: Tue, 2 Jun 2015 22:36:22 +0200
Subject: [PATCH] Introduce architechture for pagination

---
 .../java/de/thm/arsnova/aop/RangeAspect.java  | 67 +++++++++++++++++++
 .../AudienceQuestionController.java           |  6 +-
 .../LecturerQuestionController.java           | 23 ++++---
 .../controller/PaginationController.java      | 28 ++++++++
 .../arsnova/controller/SessionController.java | 13 ++--
 .../java/de/thm/arsnova/dao/CouchDBDao.java   | 38 +++++------
 .../java/de/thm/arsnova/dao/IDatabaseDao.java | 30 ++++-----
 .../arsnova/services/IQuestionService.java    | 13 ++--
 .../thm/arsnova/services/ISessionService.java |  8 +--
 .../thm/arsnova/services/QuestionService.java | 43 ++++++------
 .../thm/arsnova/services/SessionService.java  | 16 ++---
 .../java/de/thm/arsnova/web/Pagination.java   | 31 +++++++++
 .../webapp/WEB-INF/spring/arsnova-servlet.xml |  6 +-
 .../de/thm/arsnova/dao/StubDatabaseDao.java   | 30 ++++-----
 .../arsnova/services/QuestionServiceTest.java |  4 +-
 15 files changed, 247 insertions(+), 109 deletions(-)
 create mode 100644 src/main/java/de/thm/arsnova/aop/RangeAspect.java
 create mode 100644 src/main/java/de/thm/arsnova/controller/PaginationController.java
 create mode 100644 src/main/java/de/thm/arsnova/web/Pagination.java

diff --git a/src/main/java/de/thm/arsnova/aop/RangeAspect.java b/src/main/java/de/thm/arsnova/aop/RangeAspect.java
new file mode 100644
index 000000000..3ab757f68
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/aop/RangeAspect.java
@@ -0,0 +1,67 @@
+/*
+ * 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.aop;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Component;
+
+import de.thm.arsnova.controller.PaginationController;
+
+@Component
+@Aspect
+@Profile("!test")
+public class RangeAspect {
+	@Autowired
+	private HttpServletRequest request;
+
+	private final Pattern rangePattern = Pattern.compile("^items=([0-9]+)-([0-9]+)?$");
+
+	private static final Logger logger = LoggerFactory.getLogger(RangeAspect.class);
+
+	/** Sets start and end parameters based on range header
+	 *
+	 * @param controller
+	 */
+	@Before("execution(* de.thm.arsnova.controller.*.*(..)) && this(controller) && @annotation(de.thm.arsnova.web.Pagination)")
+	public void parsePaginationRange(final PaginationController controller) {
+		String rangeHeader = request.getHeader("Range");
+		Matcher matcher = null;
+		if (rangeHeader != null) {
+			matcher = rangePattern.matcher(rangeHeader);
+		}
+
+		if (matcher != null && matcher.matches()) {
+			int start = matcher.group(1) != null ? Integer.valueOf(matcher.group(1)) : -1;
+			int end = matcher.group(2) != null ? Integer.valueOf(matcher.group(2)) : -1;
+			logger.debug("Pagination: {}-{}", start, end);
+			controller.setRange(start, end);
+		} else {
+			controller.setRange(-1, -1);
+		}
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/controller/AudienceQuestionController.java b/src/main/java/de/thm/arsnova/controller/AudienceQuestionController.java
index 2202338ca..660c45381 100644
--- a/src/main/java/de/thm/arsnova/controller/AudienceQuestionController.java
+++ b/src/main/java/de/thm/arsnova/controller/AudienceQuestionController.java
@@ -36,13 +36,14 @@ import de.thm.arsnova.entities.transport.InterposedQuestion;
 import de.thm.arsnova.exceptions.BadRequestException;
 import de.thm.arsnova.services.IQuestionService;
 import de.thm.arsnova.web.DeprecatedApi;
+import de.thm.arsnova.web.Pagination;
 
 /**
  * Handles requests related to audience questions, which are also called interposed or feedback questions.
  */
 @RestController
 @RequestMapping("/audiencequestion")
-public class AudienceQuestionController extends AbstractController {
+public class AudienceQuestionController extends PaginationController {
 
 	public static final Logger LOGGER = LoggerFactory.getLogger(AudienceQuestionController.class);
 
@@ -62,8 +63,9 @@ public class AudienceQuestionController extends AbstractController {
 	}
 
 	@RequestMapping(value = "/", method = RequestMethod.GET)
+	@Pagination
 	public List<InterposedQuestion> getInterposedQuestions(@RequestParam final String sessionkey) {
-		return InterposedQuestion.fromList(questionService.getInterposedQuestions(sessionkey));
+		return InterposedQuestion.fromList(questionService.getInterposedQuestions(sessionkey, offset, limit));
 	}
 
 	@RequestMapping(value = "/{questionId}", method = RequestMethod.GET)
diff --git a/src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java b/src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java
index 59df5aaa8..8f4735080 100644
--- a/src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java
+++ b/src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java
@@ -43,13 +43,14 @@ import de.thm.arsnova.exceptions.NoContentException;
 import de.thm.arsnova.exceptions.NotFoundException;
 import de.thm.arsnova.services.IQuestionService;
 import de.thm.arsnova.web.DeprecatedApi;
+import de.thm.arsnova.web.Pagination;
 
 /**
  * Handles requests related to questions teachers are asking their students.
  */
 @RestController
 @RequestMapping("/lecturerquestion")
-public class LecturerQuestionController extends AbstractController {
+public class LecturerQuestionController extends PaginationController {
 
 	public static final Logger LOGGER = LoggerFactory.getLogger(LecturerQuestionController.class);
 
@@ -143,10 +144,10 @@ public class LecturerQuestionController extends AbstractController {
 		}
 
 		if (lectureQuestionsOnly) {
-			questions = questionService.getLectureQuestions(sessionkey);
+			questions = questionService.getLectureQuestions(sessionkey, -1, -1);
 			questionService.setVotingAdmissions(sessionkey, disable, questions);
 		} else if (preparationQuestionsOnly) {
-			questions = questionService.getPreparationQuestions(sessionkey);
+			questions = questionService.getPreparationQuestions(sessionkey, -1, -1);
 			questionService.setVotingAdmissions(sessionkey, disable, questions);
 		} else {
 			questionService.setVotingAdmissionForAllQuestions(sessionkey, disable);
@@ -180,10 +181,10 @@ public class LecturerQuestionController extends AbstractController {
 		}
 
 		if (lectureQuestionsOnly) {
-			questions = questionService.getLectureQuestions(sessionkey);
+			questions = questionService.getLectureQuestions(sessionkey, -1, -1);
 			questionService.publishQuestions(sessionkey, publish, questions);
 		} else if (preparationQuestionsOnly) {
-			questions = questionService.getPreparationQuestions(sessionkey);
+			questions = questionService.getPreparationQuestions(sessionkey, -1, -1);
 			questionService.publishQuestions(sessionkey, publish, questions);
 		} else {
 			questionService.publishAll(sessionkey, p);
@@ -215,6 +216,7 @@ public class LecturerQuestionController extends AbstractController {
 	}
 
 	@RequestMapping(value = "/", method = RequestMethod.GET)
+	@Pagination
 	public List<Question> getSkillQuestions(
 			@RequestParam final String sessionkey,
 			@RequestParam(value = "lecturequestionsonly", defaultValue = "false") final boolean lectureQuestionsOnly,
@@ -224,13 +226,13 @@ public class LecturerQuestionController extends AbstractController {
 			) {
 		List<Question> questions;
 		if (lectureQuestionsOnly) {
-			questions = questionService.getLectureQuestions(sessionkey);
+			questions = questionService.getLectureQuestions(sessionkey, offset, limit);
 		} else if (flashcardsOnly) {
-			questions = questionService.getFlashcards(sessionkey);
+			questions = questionService.getFlashcards(sessionkey, offset, limit);
 		} else if (preparationQuestionsOnly) {
-			questions = questionService.getPreparationQuestions(sessionkey);
+			questions = questionService.getPreparationQuestions(sessionkey, offset, limit);
 		} else {
-			questions = questionService.getSkillQuestions(sessionkey);
+			questions = questionService.getSkillQuestions(sessionkey, offset, limit);
 		}
 		if (questions == null || questions.isEmpty()) {
 			response.setStatus(HttpStatus.NO_CONTENT.value());
@@ -484,8 +486,9 @@ public class LecturerQuestionController extends AbstractController {
 	}
 
 	@RequestMapping(value = "/{questionId}/freetextanswer/", method = RequestMethod.GET)
+	@Pagination
 	public List<Answer> getFreetextAnswers(@PathVariable final String questionId) {
-		return questionService.getFreetextAnswers(questionId);
+		return questionService.getFreetextAnswers(questionId, offset, limit);
 	}
 
 	@DeprecatedApi
diff --git a/src/main/java/de/thm/arsnova/controller/PaginationController.java b/src/main/java/de/thm/arsnova/controller/PaginationController.java
new file mode 100644
index 000000000..9b1c7db09
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/controller/PaginationController.java
@@ -0,0 +1,28 @@
+/*
+ * 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.controller;
+
+public class PaginationController extends AbstractController {
+	protected int offset = -1;
+	protected int limit = -1;
+
+	public void setRange(int start, int end) {
+		this.offset = start;
+		this.limit = end != -1 && start <= end ? end - start + 1 : -1;
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/controller/SessionController.java b/src/main/java/de/thm/arsnova/controller/SessionController.java
index 4a75665e8..3ef4c0363 100644
--- a/src/main/java/de/thm/arsnova/controller/SessionController.java
+++ b/src/main/java/de/thm/arsnova/controller/SessionController.java
@@ -50,13 +50,14 @@ import de.thm.arsnova.services.SessionService.SessionInfoShortNameComparator;
 import de.thm.arsnova.services.SessionService.SessionNameComparator;
 import de.thm.arsnova.services.SessionService.SessionShortNameComparator;
 import de.thm.arsnova.web.DeprecatedApi;
+import de.thm.arsnova.web.Pagination;
 
 /**
  * Handles requests related to ARSnova sessions.
  */
 @RestController
 @RequestMapping("/session")
-public class SessionController extends AbstractController {
+public class SessionController extends PaginationController {
 
 	public static final Logger LOGGER = LoggerFactory.getLogger(SessionController.class);
 
@@ -117,6 +118,7 @@ public class SessionController extends AbstractController {
 	}
 
 	@RequestMapping(value = "/", method = RequestMethod.GET)
+	@Pagination
 	public List<Session> getSessions(
 			@RequestParam(value = "ownedonly", defaultValue = "false") final boolean ownedOnly,
 			@RequestParam(value = "visitedonly", defaultValue = "false") final boolean visitedOnly,
@@ -128,9 +130,9 @@ public class SessionController extends AbstractController {
 		/* TODO implement all parameter combinations, implement use of user parameter */
 		try {
 			if (ownedOnly && !visitedOnly) {
-				sessions = sessionService.getMySessions();
+				sessions = sessionService.getMySessions(offset, limit);
 			} else if (visitedOnly && !ownedOnly) {
-				sessions = sessionService.getMyVisitedSessions();
+				sessions = sessionService.getMyVisitedSessions(offset, limit);
 			} else {
 				response.setStatus(HttpStatus.NOT_IMPLEMENTED.value());
 				return null;
@@ -160,6 +162,7 @@ public class SessionController extends AbstractController {
 	 * @return
 	 */
 	@RequestMapping(value = "/", method = RequestMethod.GET, params = "statusonly=true")
+	@Pagination
 	public List<SessionInfo> getMySessions(
 			@RequestParam(value = "visitedonly", defaultValue = "false") final boolean visitedOnly,
 			@RequestParam(value = "sortby", defaultValue = "name") final String sortby,
@@ -167,9 +170,9 @@ public class SessionController extends AbstractController {
 			) {
 		List<SessionInfo> sessions;
 		if (!visitedOnly) {
-			sessions = sessionService.getMySessionsInfo();
+			sessions = sessionService.getMySessionsInfo(offset, limit);
 		} else {
-			sessions = sessionService.getMyVisitedSessionsInfo();
+			sessions = sessionService.getMyVisitedSessionsInfo(offset, limit);
 		}
 
 		if (sessions == null || sessions.isEmpty()) {
diff --git a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java b/src/main/java/de/thm/arsnova/dao/CouchDBDao.java
index fe956fbcf..7f739dfee 100644
--- a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java
+++ b/src/main/java/de/thm/arsnova/dao/CouchDBDao.java
@@ -152,7 +152,7 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 	}
 
 	@Override
-	public List<Session> getMySessions(final User user) {
+	public List<Session> getMySessions(final User user, final int start, final int limit) {
 		final NovaView view = new NovaView("session/by_creator");
 		view.setStartKeyArray(user.getUsername());
 		view.setEndKeyArray(user.getUsername(), "{}");
@@ -227,8 +227,8 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 	}
 
 	@Override
-	public List<SessionInfo> getMySessionsInfo(final User user) {
-		final List<Session> sessions = this.getMySessions(user);
+	public List<SessionInfo> getMySessionsInfo(final User user, final int start, final int limit) {
+		final List<Session> sessions = this.getMySessions(user, start, limit);
 		if (sessions.isEmpty()) {
 			return new ArrayList<SessionInfo>();
 		}
@@ -404,14 +404,14 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 
 	@Cacheable("skillquestions")
 	@Override
-	public List<Question> getSkillQuestionsForUsers(final Session session) {
+	public List<Question> getSkillQuestionsForUsers(final Session session, final int start, final int limit) {
 		String viewName = "skill_question/by_session_for_all_full";
 		return getQuestions(new NovaView(viewName), session);
 	}
 
 	@Cacheable("skillquestions")
 	@Override
-	public List<Question> getSkillQuestionsForTeachers(final Session session) {
+	public List<Question> getSkillQuestionsForTeachers(final Session session, final int start, final int limit) {
 		String viewName = "skill_question/by_session_sorted_by_subject_and_text";
 		return getQuestions(new NovaView(viewName), session);
 	}
@@ -1010,7 +1010,7 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 	}
 
 	@Override
-	public List<Answer> getFreetextAnswers(final String questionId) {
+	public List<Answer> getFreetextAnswers(final String questionId, final int start, final int limit) {
 		final List<Answer> answers = new ArrayList<Answer>();
 		final NovaView view = new NovaView("skill_question/freetext_answers_full");
 		view.setKey(questionId);
@@ -1142,7 +1142,7 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 	}
 
 	@Override
-	public List<InterposedQuestion> getInterposedQuestions(final Session session) {
+	public List<InterposedQuestion> getInterposedQuestions(final Session session, final int start, final int limit) {
 		final NovaView view = new NovaView("interposed_question/by_session_full");
 		view.setKey(session.get_id());
 		final ViewResults questions = getDatabase().view(view);
@@ -1153,7 +1153,7 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 	}
 
 	@Override
-	public List<InterposedQuestion> getInterposedQuestions(final Session session, final User user) {
+	public List<InterposedQuestion> getInterposedQuestions(final Session session, final User user, final int start, final int limit) {
 		final NovaView view = new NovaView("interposed_question/by_session_and_creator");
 		view.setKey(session.get_id(), user.getUsername());
 		final ViewResults questions = getDatabase().view(view);
@@ -1276,7 +1276,7 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 	}
 
 	@Override
-	public List<Session> getMyVisitedSessions(final User user) {
+	public List<Session> getMyVisitedSessions(final User user, final int start, final int limit) {
 		final NovaView view = new NovaView("logged_in/visited_sessions_by_user");
 		view.setKey(user.getUsername());
 		final ViewResults sessions = getDatabase().view(view);
@@ -1336,8 +1336,8 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 	}
 
 	@Override
-	public List<SessionInfo> getMyVisitedSessionsInfo(final User user) {
-		List<Session> sessions = this.getMyVisitedSessions(user);
+	public List<SessionInfo> getMyVisitedSessionsInfo(final User user, final int start, final int limit) {
+		List<Session> sessions = this.getMyVisitedSessions(user, start, limit);
 		if (sessions.isEmpty()) {
 			return new ArrayList<SessionInfo>();
 		}
@@ -1519,39 +1519,39 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 
 	@Cacheable("lecturequestions")
 	@Override
-	public List<Question> getLectureQuestionsForUsers(final Session session) {
+	public List<Question> getLectureQuestionsForUsers(final Session session, final int start, final int limit) {
 		String viewName = "skill_question/lecture_question_by_session_for_all";
 		return getQuestions(new NovaView(viewName), session);
 	}
 
 	@Override
-	public List<Question> getLectureQuestionsForTeachers(final Session session) {
+	public List<Question> getLectureQuestionsForTeachers(final Session session, final int start, final int limit) {
 		String viewName = "skill_question/lecture_question_by_session";
 		return getQuestions(new NovaView(viewName), session);
 	}
 
 	@Cacheable("flashcardquestions")
 	@Override
-	public List<Question> getFlashcardsForUsers(final Session session) {
+	public List<Question> getFlashcardsForUsers(final Session session, final int start, final int limit) {
 		String viewName = "skill_question/flashcard_by_session_for_all";
 		return getQuestions(new NovaView(viewName), session);
 	}
 
 	@Override
-	public List<Question> getFlashcardsForTeachers(final Session session) {
+	public List<Question> getFlashcardsForTeachers(final Session session, final int start, final int limit) {
 		String viewName = "skill_question/flashcard_by_session";
 		return getQuestions(new NovaView(viewName), session);
 	}
 
 	@Cacheable("preparationquestions")
 	@Override
-	public List<Question> getPreparationQuestionsForUsers(final Session session) {
+	public List<Question> getPreparationQuestionsForUsers(final Session session, final int start, final int limit) {
 		String viewName = "skill_question/preparation_question_by_session_for_all";
 		return getQuestions(new NovaView(viewName), session);
 	}
 
 	@Override
-	public List<Question> getPreparationQuestionsForTeachers(final Session session) {
+	public List<Question> getPreparationQuestionsForTeachers(final Session session, final int start, final int limit) {
 		String viewName = "skill_question/preparation_question_by_session";
 		return getQuestions(new NovaView(viewName), session);
 	}
@@ -1672,14 +1672,14 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 	public List<String> getUnAnsweredLectureQuestionIds(final Session session, final User user) {
 		final NovaView view = new NovaView("answer/variant_by_user_and_piround");
 		view.setKey(user.getUsername(), session.get_id(), "lecture");
-		return collectUnansweredQuestionIdsByPiRound(getDatabaseDao().getLectureQuestionsForUsers(session), view);
+		return collectUnansweredQuestionIdsByPiRound(getDatabaseDao().getLectureQuestionsForUsers(session, -1, -1), view);
 	}
 
 	@Override
 	public List<String> getUnAnsweredPreparationQuestionIds(final Session session, final User user) {
 		final NovaView view = new NovaView("answer/variant_by_user_and_piround");
 		view.setKey(user.getUsername(), session.get_id(), "preparation");
-		return collectUnansweredQuestionIdsByPiRound(getDatabaseDao().getPreparationQuestionsForUsers(session), view);
+		return collectUnansweredQuestionIdsByPiRound(getDatabaseDao().getPreparationQuestionsForUsers(session, -1, -1), view);
 	}
 
 	private List<String> collectUnansweredQuestionIds(
diff --git a/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java b/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java
index f910ecbba..407d08124 100644
--- a/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java
+++ b/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java
@@ -40,7 +40,7 @@ import de.thm.arsnova.entities.transport.ImportExportSession;
 public interface IDatabaseDao {
 	Session getSessionFromKeyword(String keyword);
 
-	List<Session> getMySessions(User user);
+	List<Session> getMySessions(User user, final int start, final int limit);
 
 	List<Session> getPublicPoolSessions();
 
@@ -65,9 +65,9 @@ public interface IDatabaseDao {
 	@Deprecated
 	List<Question> getSkillQuestions(User user, Session session);
 
-	List<Question> getSkillQuestionsForUsers(Session session);
+	List<Question> getSkillQuestionsForUsers(Session session, final int start, final int limit);
 
-	List<Question> getSkillQuestionsForTeachers(Session session);
+	List<Question> getSkillQuestionsForTeachers(Session session, final int start, final int limit);
 
 	int getSkillQuestionCount(Session session);
 
@@ -97,7 +97,7 @@ public interface IDatabaseDao {
 
 	int getAbstentionAnswerCount(String questionId);
 
-	List<Answer> getFreetextAnswers(String questionId);
+	List<Answer> getFreetextAnswers(String questionId, final int start, final int limit);
 
 	List<Answer> getMyAnswers(User me, Session session);
 
@@ -109,15 +109,15 @@ public interface IDatabaseDao {
 
 	InterposedReadingCount getInterposedReadingCount(Session session, User user);
 
-	List<InterposedQuestion> getInterposedQuestions(Session session);
+	List<InterposedQuestion> getInterposedQuestions(Session session, final int start, final int limit);
 
-	List<InterposedQuestion> getInterposedQuestions(Session session, User user);
+	List<InterposedQuestion> getInterposedQuestions(Session session, User user, final int start, final int limit);
 
 	InterposedQuestion getInterposedQuestion(String questionId);
 
 	void markInterposedQuestionAsRead(InterposedQuestion question);
 
-	List<Session> getMyVisitedSessions(User user);
+	List<Session> getMyVisitedSessions(User user, final int start, final int limit);
 
 	Question updateQuestion(Question question);
 
@@ -139,17 +139,17 @@ public interface IDatabaseDao {
 
 	void deleteSession(Session session);
 
-	List<Question> getLectureQuestionsForUsers(Session session);
+	List<Question> getLectureQuestionsForUsers(Session session, final int start, final int limit);
 
-	List<Question> getLectureQuestionsForTeachers(Session session);
+	List<Question> getLectureQuestionsForTeachers(Session session, final int start, final int limit);
 
-	List<Question> getFlashcardsForUsers(Session session);
+	List<Question> getFlashcardsForUsers(Session session, final int start, final int limit);
 
-	List<Question> getFlashcardsForTeachers(Session session);
+	List<Question> getFlashcardsForTeachers(Session session, final int start, final int limit);
 
-	List<Question> getPreparationQuestionsForUsers(Session session);
+	List<Question> getPreparationQuestionsForUsers(Session session, final int start, final int limit);
 
-	List<Question> getPreparationQuestionsForTeachers(Session session);
+	List<Question> getPreparationQuestionsForTeachers(Session session, final int start, final int limit);
 
 	int getLectureQuestionCount(Session session);
 
@@ -189,13 +189,13 @@ public interface IDatabaseDao {
 
 	CourseScore getLearningProgress(Session session);
 
-	List<SessionInfo> getMySessionsInfo(User user);
+	List<SessionInfo> getMySessionsInfo(User user, final int start, final int limit);
 
 	List<SessionInfo> getPublicPoolSessionsInfo();
 
 	List<SessionInfo> getMyPublicPoolSessionsInfo(final User user);
 
-	List<SessionInfo> getMyVisitedSessionsInfo(User currentUser);
+	List<SessionInfo> getMyVisitedSessionsInfo(User currentUser, final int start, final int limit);
 
 	void deleteAllPreparationAnswers(Session session);
 
diff --git a/src/main/java/de/thm/arsnova/services/IQuestionService.java b/src/main/java/de/thm/arsnova/services/IQuestionService.java
index a21d84244..ce28fdb87 100644
--- a/src/main/java/de/thm/arsnova/services/IQuestionService.java
+++ b/src/main/java/de/thm/arsnova/services/IQuestionService.java
@@ -37,7 +37,7 @@ public interface IQuestionService {
 
 	Question getQuestion(String id);
 
-	List<Question> getSkillQuestions(String sessionkey);
+	List<Question> getSkillQuestions(String sessionkey, int offset, int limit);
 
 	int getSkillQuestionCount(String sessionkey);
 
@@ -71,7 +71,8 @@ public interface IQuestionService {
 
 	int getAnswerCount(String questionId, int piRound);
 
-	List<Answer> getFreetextAnswers(String questionId);
+	List<Answer> getFreetextAnswers(String questionId, int offset, int limit);
+
 
 	List<Answer> getMyAnswers(String sessionKey);
 
@@ -83,7 +84,7 @@ public interface IQuestionService {
 
 	InterposedReadingCount getInterposedReadingCount(String sessionKey, String username);
 
-	List<InterposedQuestion> getInterposedQuestions(String sessionKey);
+	List<InterposedQuestion> getInterposedQuestions(String sessionKey, int offset, int limit);
 
 	InterposedQuestion readInterposedQuestion(String questionId);
 
@@ -103,11 +104,11 @@ public interface IQuestionService {
 
 	void deleteInterposedQuestion(String questionId);
 
-	List<Question> getLectureQuestions(String sessionkey);
+	List<Question> getLectureQuestions(String sessionkey, int offset, int limit);
 
-	List<Question> getFlashcards(String sessionkey);
+	List<Question> getFlashcards(String sessionkey, int offset, int limit);
 
-	List<Question> getPreparationQuestions(String sessionkey);
+	List<Question> getPreparationQuestions(String sessionkey, int offset, int limit);
 
 	int getLectureQuestionCount(String sessionkey);
 
diff --git a/src/main/java/de/thm/arsnova/services/ISessionService.java b/src/main/java/de/thm/arsnova/services/ISessionService.java
index ab4a395af..22ccbf90b 100644
--- a/src/main/java/de/thm/arsnova/services/ISessionService.java
+++ b/src/main/java/de/thm/arsnova/services/ISessionService.java
@@ -42,9 +42,9 @@ public interface ISessionService {
 
 	String generateKeyword();
 
-	List<Session> getMySessions();
+	List<Session> getMySessions(int offset, int limit);
 
-	List<Session> getMyVisitedSessions();
+	List<Session> getMyVisitedSessions(int offset, int limit);
 
 	int countSessions(List<Course> courses);
 
@@ -64,13 +64,13 @@ public interface ISessionService {
 
 	LearningProgressValues getMyLearningProgress(String sessionkey, String progressType, String questionVariant);
 
-	List<SessionInfo> getMySessionsInfo();
+	List<SessionInfo> getMySessionsInfo(int offset, int limit);
 
 	List<SessionInfo> getPublicPoolSessionsInfo();
 
 	List<SessionInfo> getMyPublicPoolSessionsInfo();
 
-	List<SessionInfo> getMyVisitedSessionsInfo();
+	List<SessionInfo> getMyVisitedSessionsInfo(int offset, int limit);
 
 	SessionInfo importSession(ImportExportSession session);
 
diff --git a/src/main/java/de/thm/arsnova/services/QuestionService.java b/src/main/java/de/thm/arsnova/services/QuestionService.java
index 5edb106f5..1158f57c6 100644
--- a/src/main/java/de/thm/arsnova/services/QuestionService.java
+++ b/src/main/java/de/thm/arsnova/services/QuestionService.java
@@ -27,7 +27,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Timer;
 import java.util.TimerTask;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -104,13 +103,13 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public List<Question> getSkillQuestions(final String sessionkey) {
+	public List<Question> getSkillQuestions(final String sessionkey, final int offset, final int limit) {
 		final Session session = getSession(sessionkey);
 		final User user = userService.getCurrentUser();
 		if (session.isCreator(user)) {
-			return databaseDao.getSkillQuestionsForTeachers(session);
+			return databaseDao.getSkillQuestionsForTeachers(session, offset, limit);
 		} else {
-			return databaseDao.getSkillQuestionsForUsers(session);
+			return databaseDao.getSkillQuestionsForUsers(session, offset, limit);
 		}
 	}
 
@@ -486,7 +485,7 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 			throw new NotFoundException();
 		}
 		return "freetext".equals(question.getQuestionType())
-				? getFreetextAnswers(questionId)
+				? getFreetextAnswers(questionId, -1, -1)
 						: databaseDao.getAnswers(question, piRound);
 	}
 
@@ -498,7 +497,7 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 			throw new NotFoundException();
 		}
 		if ("freetext".equals(question.getQuestionType())) {
-			return getFreetextAnswers(questionId);
+			return getFreetextAnswers(questionId, -1, -1);
 		} else {
 			return databaseDao.getAnswers(question);
 		}
@@ -512,7 +511,7 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 			throw new NotFoundException();
 		}
 		if ("freetext".equals(question.getQuestionType())) {
-			return getFreetextAnswers(questionId);
+			return getFreetextAnswers(questionId, -1, -1);
 		} else {
 			return databaseDao.getAllAnswers(question);
 		}
@@ -564,8 +563,8 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public List<Answer> getFreetextAnswers(final String questionId) {
-		final List<Answer> answers = databaseDao.getFreetextAnswers(questionId);
+	public List<Answer> getFreetextAnswers(final String questionId, final int offset, final int limit) {
+		final List<Answer> answers = databaseDao.getFreetextAnswers(questionId, offset, limit);
 		if (answers == null) {
 			throw new NotFoundException();
 		}
@@ -582,7 +581,7 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	public List<Answer> getMyAnswers(final String sessionKey) {
 		final Session session = getSession(sessionKey);
 		// Load questions first because we are only interested in answers of the latest piRound.
-		final List<Question> questions = databaseDao.getSkillQuestionsForUsers(session);
+		final List<Question> questions = databaseDao.getSkillQuestionsForUsers(session, -1, -1);
 		final Map<String, Question> questionIdToQuestion = new HashMap<String, Question>();
 		for (final Question question : questions) {
 			questionIdToQuestion.put(question.get_id(), question);
@@ -644,13 +643,13 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public List<InterposedQuestion> getInterposedQuestions(final String sessionKey) {
+	public List<InterposedQuestion> getInterposedQuestions(final String sessionKey, final int offset, final int limit) {
 		final Session session = this.getSession(sessionKey);
 		final User user = getCurrentUser();
 		if (session.isCreator(user)) {
-			return databaseDao.getInterposedQuestions(session);
+			return databaseDao.getInterposedQuestions(session, offset, limit);
 		} else {
-			return databaseDao.getInterposedQuestions(session, user);
+			return databaseDao.getInterposedQuestions(session, user, offset, limit);
 		}
 	}
 
@@ -800,7 +799,7 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public List<Question> getLectureQuestions(final String sessionkey) {
+	public List<Question> getLectureQuestions(final String sessionkey, final int offset, final int limit) {
 		final Session session = getSession(sessionkey);
 		SortOrder subjectSortOrder = databaseDao.getSortOrder(session.get_id(), "lecture", "");
 		if (subjectSortOrder == null) {
@@ -811,27 +810,27 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 		}
 		final User user = userService.getCurrentUser();
 		if (session.isCreator(user)) {
-			return databaseDao.getLectureQuestionsForTeachers(session);
+			return databaseDao.getLectureQuestionsForTeachers(session, offset, limit);
 		} else {
-			return databaseDao.getLectureQuestionsForUsers(session);
+			return databaseDao.getLectureQuestionsForUsers(session, offset, limit);
 		}
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public List<Question> getFlashcards(final String sessionkey) {
+	public List<Question> getFlashcards(final String sessionkey, final int offset, final int limit) {
 		final Session session = getSession(sessionkey);
 		final User user = userService.getCurrentUser();
 		if (session.isCreator(user)) {
-			return databaseDao.getFlashcardsForTeachers(session);
+			return databaseDao.getFlashcardsForTeachers(session, offset, limit);
 		} else {
-			return databaseDao.getFlashcardsForUsers(session);
+			return databaseDao.getFlashcardsForUsers(session, offset, limit);
 		}
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public List<Question> getPreparationQuestions(final String sessionkey) {
+	public List<Question> getPreparationQuestions(final String sessionkey, final int offset, final int limit) {
 		final Session session = getSession(sessionkey);
 		SortOrder subjectSortOrder = databaseDao.getSortOrder(session.get_id(), "preparation", "");
 		if (subjectSortOrder == null) {
@@ -842,9 +841,9 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 		}
 		final User user = userService.getCurrentUser();
 		if (session.isCreator(user)) {
-			return databaseDao.getPreparationQuestionsForTeachers(session);
+			return databaseDao.getPreparationQuestionsForTeachers(session, offset, limit);
 		} else {
-			return databaseDao.getPreparationQuestionsForUsers(session);
+			return databaseDao.getPreparationQuestionsForUsers(session, offset, limit);
 		}
 	}
 
diff --git a/src/main/java/de/thm/arsnova/services/SessionService.java b/src/main/java/de/thm/arsnova/services/SessionService.java
index f9a49bf2f..b404910b3 100644
--- a/src/main/java/de/thm/arsnova/services/SessionService.java
+++ b/src/main/java/de/thm/arsnova/services/SessionService.java
@@ -187,8 +187,8 @@ public class SessionService implements ISessionService, ApplicationEventPublishe
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public List<Session> getMySessions() {
-		return databaseDao.getMySessions(userService.getCurrentUser());
+	public List<Session> getMySessions(final int offset, final int limit) {
+		return databaseDao.getMySessions(userService.getCurrentUser(), offset, limit);
 	}
 
 	@Override
@@ -205,21 +205,21 @@ public class SessionService implements ISessionService, ApplicationEventPublishe
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public List<SessionInfo> getMySessionsInfo() {
+	public List<SessionInfo> getMySessionsInfo(final int offset, final int limit) {
 		final User user = userService.getCurrentUser();
-		return databaseDao.getMySessionsInfo(user);
+		return databaseDao.getMySessionsInfo(user, offset, limit);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public List<Session> getMyVisitedSessions() {
-		return databaseDao.getMyVisitedSessions(userService.getCurrentUser());
+	public List<Session> getMyVisitedSessions(final int offset, final int limit) {
+		return databaseDao.getMyVisitedSessions(userService.getCurrentUser(), offset, limit);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public List<SessionInfo> getMyVisitedSessionsInfo() {
-		return databaseDao.getMyVisitedSessionsInfo(userService.getCurrentUser());
+	public List<SessionInfo> getMyVisitedSessionsInfo(final int offset, final int limit) {
+		return databaseDao.getMyVisitedSessionsInfo(userService.getCurrentUser(), offset, limit);
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/web/Pagination.java b/src/main/java/de/thm/arsnova/web/Pagination.java
new file mode 100644
index 000000000..e8f60df65
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/web/Pagination.java
@@ -0,0 +1,31 @@
+/*
+ * 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.web;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+@Documented
+public @interface Pagination {
+
+}
diff --git a/src/main/webapp/WEB-INF/spring/arsnova-servlet.xml b/src/main/webapp/WEB-INF/spring/arsnova-servlet.xml
index 2375d536e..5e4bf336a 100644
--- a/src/main/webapp/WEB-INF/spring/arsnova-servlet.xml
+++ b/src/main/webapp/WEB-INF/spring/arsnova-servlet.xml
@@ -1,17 +1,19 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://www.springframework.org/schema/beans"
 	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:aop="http://www.springframework.org/schema/aop"
 	xmlns:context="http://www.springframework.org/schema/context"
 	xmlns:mvc="http://www.springframework.org/schema/mvc"
 	xmlns:p="http://www.springframework.org/schema/p"
 	xsi:schemaLocation="
+		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
 		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
 		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
 		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
 
 	<!-- ARSnova Servlet Context -->
 
-	<context:component-scan base-package="de.thm.arsnova.controller,de.thm.arsnova.web" />
+	<context:component-scan base-package="de.thm.arsnova.aop,de.thm.arsnova.controller,de.thm.arsnova.web" />
 
 	<mvc:annotation-driven
 		content-negotiation-manager="mvcContentNegotiationManager" />
@@ -21,6 +23,8 @@
 		<bean class="de.thm.arsnova.web.DeprecatedApiInterceptorHandler" />
 	</mvc:interceptors>
 
+	<aop:aspectj-autoproxy />
+
 	<bean id="propertyPlaceholderConfigurer"
 		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
 		p:ignoreUnresolvablePlaceholders="false" p:ignoreResourceNotFound="true">
diff --git a/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java b/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java
index 32ec1204a..012593359 100644
--- a/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java
+++ b/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java
@@ -155,7 +155,7 @@ public class StubDatabaseDao implements IDatabaseDao {
 	}
 
 	@Override
-	public List<Session> getMySessions(User user) {
+	public List<Session> getMySessions(User user, final int start, final int limit) {
 		// TODO Auto-generated method stub
 		return null;
 	}
@@ -209,7 +209,7 @@ public class StubDatabaseDao implements IDatabaseDao {
 	}
 
 	@Override
-	public List<Answer> getFreetextAnswers(String questionId) {
+	public List<Answer> getFreetextAnswers(String questionId, final int start, final int limit) {
 		// TODO Auto-generated method stub
 		return null;
 	}
@@ -232,7 +232,7 @@ public class StubDatabaseDao implements IDatabaseDao {
 	}
 
 	@Override
-	public List<InterposedQuestion> getInterposedQuestions(Session session) {
+	public List<InterposedQuestion> getInterposedQuestions(Session session, final int start, final int limit) {
 		// TODO Auto-generated method stub
 		return null;
 	}
@@ -254,7 +254,7 @@ public class StubDatabaseDao implements IDatabaseDao {
 	}
 
 	@Override
-	public List<Session> getMyVisitedSessions(User user) {
+	public List<Session> getMyVisitedSessions(User user, final int start, final int limit) {
 		// TODO Auto-generated method stub
 		return null;
 	}
@@ -445,7 +445,7 @@ public class StubDatabaseDao implements IDatabaseDao {
 	}
 
 	@Override
-	public List<InterposedQuestion> getInterposedQuestions(Session session, User user) {
+	public List<InterposedQuestion> getInterposedQuestions(Session session, User user, final int start, final int limit) {
 		// TODO Auto-generated method stub
 		return null;
 	}
@@ -463,13 +463,13 @@ public class StubDatabaseDao implements IDatabaseDao {
 	}
 
 	@Override
-	public List<SessionInfo> getMySessionsInfo(User user) {
+	public List<SessionInfo> getMySessionsInfo(User user, final int start, final int limit) {
 		// TODO Auto-generated method stub
 		return null;
 	}
 
 	@Override
-	public List<SessionInfo> getMyVisitedSessionsInfo(User currentUser) {
+	public List<SessionInfo> getMyVisitedSessionsInfo(User currentUser, final int start, final int limit) {
 		// TODO Auto-generated method stub
 		return null;
 	}
@@ -499,49 +499,49 @@ public class StubDatabaseDao implements IDatabaseDao {
 	}
 
 	@Override
-	public List<Question> getSkillQuestionsForUsers(Session session) {
+	public List<Question> getSkillQuestionsForUsers(Session session, final int start, final int limit) {
 		// TODO Auto-generated method stub
 		return null;
 	}
 
 	@Override
-	public List<Question> getSkillQuestionsForTeachers(Session session) {
+	public List<Question> getSkillQuestionsForTeachers(Session session, final int start, final int limit) {
 		// TODO Auto-generated method stub
 		return null;
 	}
 
 	@Override
-	public List<Question> getLectureQuestionsForUsers(Session session) {
+	public List<Question> getLectureQuestionsForUsers(Session session, final int start, final int limit) {
 		// TODO Auto-generated method stub
 		return null;
 	}
 
 	@Override
-	public List<Question> getLectureQuestionsForTeachers(Session session) {
+	public List<Question> getLectureQuestionsForTeachers(Session session, final int start, final int limit) {
 		// TODO Auto-generated method stub
 		return null;
 	}
 
 	@Override
-	public List<Question> getFlashcardsForUsers(Session session) {
+	public List<Question> getFlashcardsForUsers(Session session, final int start, final int limit) {
 		// TODO Auto-generated method stub
 		return null;
 	}
 
 	@Override
-	public List<Question> getFlashcardsForTeachers(Session session) {
+	public List<Question> getFlashcardsForTeachers(Session session, final int start, final int limit) {
 		// TODO Auto-generated method stub
 		return null;
 	}
 
 	@Override
-	public List<Question> getPreparationQuestionsForUsers(Session session) {
+	public List<Question> getPreparationQuestionsForUsers(Session session, final int start, final int limit) {
 		// TODO Auto-generated method stub
 		return null;
 	}
 
 	@Override
-	public List<Question> getPreparationQuestionsForTeachers(Session session) {
+	public List<Question> getPreparationQuestionsForTeachers(Session session, final int start, final int limit) {
 		// TODO Auto-generated method stub
 		return null;
 	}
diff --git a/src/test/java/de/thm/arsnova/services/QuestionServiceTest.java b/src/test/java/de/thm/arsnova/services/QuestionServiceTest.java
index a843bd113..b8d7c8082 100644
--- a/src/test/java/de/thm/arsnova/services/QuestionServiceTest.java
+++ b/src/test/java/de/thm/arsnova/services/QuestionServiceTest.java
@@ -86,13 +86,13 @@ public class QuestionServiceTest {
 	@Test(expected = AuthenticationCredentialsNotFoundException.class)
 	public void testShouldNotReturnQuestionsIfNotAuthenticated() {
 		setAuthenticated(false, "nobody");
-		questionService.getSkillQuestions("12345678");
+		questionService.getSkillQuestions("12345678", -1, -1);
 	}
 
 	@Test(expected = NotFoundException.class)
 	public void testShouldFindQuestionsForNonExistantSession() {
 		setAuthenticated(true, "ptsr00");
-		questionService.getSkillQuestions("00000000");
+		questionService.getSkillQuestions("00000000", -1, -1);
 	}
 
 	@Test
-- 
GitLab