Commit d108c3a0 authored by Daniel Gerhardt's avatar Daniel Gerhardt

Merge branch 'refac' into 'master'

Refactor architecture layers

See merge request !71
parents e4268386 2d947098
......@@ -143,6 +143,10 @@
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-mail</artifactId>
......@@ -220,6 +224,11 @@
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
......
......@@ -17,7 +17,7 @@
*/
package de.thm.arsnova.aop;
import de.thm.arsnova.PaginationListDecorator;
import de.thm.arsnova.util.PaginationListDecorator;
import de.thm.arsnova.controller.PaginationController;
import de.thm.arsnova.services.ResponseProviderService;
import org.aspectj.lang.ProceedingJoinPoint;
......
......@@ -39,7 +39,7 @@ public class UserSessionAspect {
/** Sets current user and ARSnova session in session scoped UserSessionService
*/
@AfterReturning(
pointcut = "execution(public * de.thm.arsnova.services.SessionService.joinSession(..)) && args(keyword)",
pointcut = "execution(public * de.thm.arsnova.services.SessionService.join(..)) && args(keyword)",
returning = "session"
)
public void joinSessionAdvice(final JoinPoint jp, final String keyword, final Session session) {
......
......@@ -17,114 +17,7 @@
*/
package de.thm.arsnova.cache;
import de.thm.arsnova.events.*;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Component;
/**
* This class is used to evict caches based on events. The events carry all necessary information to clear the
* caches, e.g, for a specific session.
* This interface is used as a tag to make Spring dependency injection happy...
*/
@Component
public class CacheBuster implements ICacheBuster, NovaEventVisitor {
@CacheEvict(value = "statistics", allEntries = true)
@Override
public void visit(NewCommentEvent event) { }
@CacheEvict(value = "statistics", allEntries = true)
@Override
public void visit(DeleteCommentEvent event) { }
@Override
public void visit(NewQuestionEvent event) { }
@Override
public void visit(UnlockQuestionEvent event) { }
@Override
public void visit(UnlockQuestionsEvent newQuestionsEvent) { }
@Override
public void visit(LockQuestionEvent lockQuestionEvent) { }
@Override
public void visit(LockQuestionsEvent lockQuestionsEvent) { }
@CacheEvict(value = "answers", key = "#event.content")
@Override
public void visit(NewAnswerEvent event) { }
@Override
public void visit(DeleteAnswerEvent event) { }
@Override
public void visit(DeleteQuestionEvent event) { }
@Override
public void visit(DeleteAllQuestionsEvent event) { }
@Override
public void visit(DeleteAllQuestionsAnswersEvent event) { }
@Override
public void visit(DeleteAllPreparationAnswersEvent event) { }
@Override
public void visit(DeleteAllLectureAnswersEvent event) { }
@Override
public void visit(NewFeedbackEvent event) { }
@Override
public void visit(DeleteFeedbackForSessionsEvent event) { }
@Override
public void visit(StatusSessionEvent event) { }
@CacheEvict(value = "statistics", allEntries = true)
@Override
public void visit(ChangeLearningProgressEvent changeLearningProgress) { }
@Override
public void visit(PiRoundDelayedStartEvent piRoundDelayedStartEvent) { }
@Override
public void visit(PiRoundEndEvent piRoundEndEvent) { }
@Override
public void visit(PiRoundCancelEvent piRoundCancelEvent) { }
@Override
public void visit(PiRoundResetEvent piRoundResetEvent) { }
@CacheEvict(value = "statistics", allEntries = true)
@Override
public void visit(NewSessionEvent newSessionEvent) { }
@CacheEvict(value = "statistics", allEntries = true)
@Override
public void visit(DeleteSessionEvent deleteSessionEvent) { }
@Override
public void visit(LockVoteEvent lockVoteEvent) { }
@Override
public void visit(LockVotesEvent lockVotesEvent) { }
@Override
public void visit(UnlockVoteEvent unlockVoteEvent) { }
@Override
public void visit(UnlockVotesEvent unlockVotesEvent) { }
@Override
public void visit(FeatureChangeEvent featureChangeEvent) { }
@Override
public void visit(LockFeedbackEvent lockFeedbackEvent) { }
@Override
public void visit(FlipFlashcardsEvent flipFlashcardsEvent) { }
}
public interface CacheBuster { }
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2017 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.cache;
import de.thm.arsnova.events.*;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Component;
/**
* This class is used to evict caches based on events. The events carry all necessary information to clear the
* caches, e.g, for a specific session.
*/
@Component
public class CacheBusterImpl implements CacheBuster, ArsnovaEventVisitor {
@CacheEvict(value = "statistics", allEntries = true)
@Override
public void visit(NewCommentEvent event) { }
@CacheEvict(value = "statistics", allEntries = true)
@Override
public void visit(DeleteCommentEvent event) { }
@Override
public void visit(NewQuestionEvent event) { }
@Override
public void visit(UnlockQuestionEvent event) { }
@Override
public void visit(UnlockQuestionsEvent newQuestionsEvent) { }
@Override
public void visit(LockQuestionEvent lockQuestionEvent) { }
@Override
public void visit(LockQuestionsEvent lockQuestionsEvent) { }
@CacheEvict(value = "answerlists", key = "#event.content.id")
@Override
public void visit(NewAnswerEvent event) { }
@Override
public void visit(DeleteAnswerEvent event) { }
@Override
public void visit(DeleteQuestionEvent event) { }
@Override
public void visit(DeleteAllQuestionsEvent event) { }
@Override
public void visit(DeleteAllQuestionsAnswersEvent event) { }
@Override
public void visit(DeleteAllPreparationAnswersEvent event) { }
@Override
public void visit(DeleteAllLectureAnswersEvent event) { }
@Override
public void visit(NewFeedbackEvent event) { }
@Override
public void visit(DeleteFeedbackForSessionsEvent event) { }
@Override
public void visit(StatusSessionEvent event) { }
@CacheEvict(value = "statistics", allEntries = true)
@Override
public void visit(ChangeScoreEvent changeLearningProgress) { }
@Override
public void visit(PiRoundDelayedStartEvent piRoundDelayedStartEvent) { }
@Override
public void visit(PiRoundEndEvent piRoundEndEvent) { }
@Override
public void visit(PiRoundCancelEvent piRoundCancelEvent) { }
@Override
public void visit(PiRoundResetEvent piRoundResetEvent) { }
@CacheEvict(value = "statistics", allEntries = true)
@Override
public void visit(NewSessionEvent newSessionEvent) { }
@CacheEvict(value = "statistics", allEntries = true)
@Override
public void visit(DeleteSessionEvent deleteSessionEvent) { }
@Override
public void visit(LockVoteEvent lockVoteEvent) { }
@Override
public void visit(LockVotesEvent lockVotesEvent) { }
@Override
public void visit(UnlockVoteEvent unlockVoteEvent) { }
@Override
public void visit(UnlockVotesEvent unlockVotesEvent) { }
@Override
public void visit(FeatureChangeEvent featureChangeEvent) { }
@Override
public void visit(LockFeedbackEvent lockFeedbackEvent) { }
@Override
public void visit(FlipFlashcardsEvent flipFlashcardsEvent) { }
}
......@@ -17,8 +17,8 @@
*/
package de.thm.arsnova.cache;
import de.thm.arsnova.events.NovaEvent;
import de.thm.arsnova.events.NovaEventVisitor;
import de.thm.arsnova.events.ArsnovaEvent;
import de.thm.arsnova.events.ArsnovaEventVisitor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
......@@ -29,14 +29,14 @@ import org.springframework.stereotype.Component;
* Note that this class is necessary in order for the annotations to work.
*/
@Component
public class CacheBustListener implements ApplicationListener<NovaEvent> {
public class CacheBusterListener implements ApplicationListener<ArsnovaEvent> {
@Autowired
private ICacheBuster cacheBuster;
private CacheBuster cacheBuster;
@Override
public void onApplicationEvent(NovaEvent event) {
event.accept((NovaEventVisitor) cacheBuster);
public void onApplicationEvent(ArsnovaEvent event) {
event.accept((ArsnovaEventVisitor) cacheBuster);
}
}
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2017 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.cache;
/**
* This interface is used as a tag to make Spring dependency injection happy...
*/
public interface ICacheBuster { }
......@@ -38,31 +38,31 @@ public class ScheduledCacheBuster {
@Scheduled(initialDelay = 1000 * 25, fixedRate = 1000 * 60 * 60 * 6)
private void clearSessionCache() { }
@CacheEvict(value = "questions", allEntries = true)
@CacheEvict(value = "contents", allEntries = true)
@Scheduled(initialDelay = 1000 * 50, fixedRate = 1000 * 60 * 30)
private void clearQuestionCache() { }
@CacheEvict(value = "skillquestions", allEntries = true)
@CacheEvict(value = "contentlists", allEntries = true)
@Scheduled(initialDelay = 1000 * 75, fixedRate = 1000 * 60 * 30)
private void clearSkillQuestionCache() { }
@CacheEvict(value = "lecturequestions", allEntries = true)
@CacheEvict(value = "lecturecontentlists", allEntries = true)
@Scheduled(initialDelay = 1000 * 100, fixedRate = 1000 * 60 * 30)
private void clearLectureQuestionCache() { }
@CacheEvict(value = "preparationquestions", allEntries = true)
@CacheEvict(value = "preparationcontentlists", allEntries = true)
@Scheduled(initialDelay = 1000 * 125, fixedRate = 1000 * 60 * 30)
private void clearPreparationQuestionCache() { }
@CacheEvict(value = "flashcardquestions", allEntries = true)
@CacheEvict(value = "flashcardcontentlists", allEntries = true)
@Scheduled(initialDelay = 1000 * 150, fixedRate = 1000 * 60 * 30)
private void clearFlashcardQuestionCache() { }
@CacheEvict(value = "answers", allEntries = true)
@CacheEvict(value = "answerlists", allEntries = true)
@Scheduled(initialDelay = 1000 * 175, fixedRate = 1000 * 60 * 15)
private void clearAnswerCache() { }
@CacheEvict(value = "learningprogress", allEntries = true)
@CacheEvict(value = "score", allEntries = true)
@Scheduled(initialDelay = 1000 * 200, fixedRate = 1000 * 60 * 15)
private void clearLearningProgressCache() { }
......
......@@ -20,26 +20,18 @@ package de.thm.arsnova.config;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import de.thm.arsnova.ImageUtils;
import de.thm.arsnova.util.ImageUtils;
import de.thm.arsnova.connector.client.ConnectorClient;
import de.thm.arsnova.connector.client.ConnectorClientImpl;
import de.thm.arsnova.entities.*;
import de.thm.arsnova.entities.serialization.CouchDbDocumentModule;
import de.thm.arsnova.entities.serialization.CouchDbObjectMapperFactory;
import de.thm.arsnova.entities.serialization.View;
import de.thm.arsnova.persistance.*;
import de.thm.arsnova.persistance.couchdb.*;
import de.thm.arsnova.persistance.couchdb.InitializingCouchDbConnector;
import de.thm.arsnova.socket.ARSnovaSocket;
import de.thm.arsnova.socket.ARSnovaSocketIOServer;
import de.thm.arsnova.socket.ARSnovaSocketListener;
import de.thm.arsnova.websocket.ArsnovaSocketioServer;
import de.thm.arsnova.websocket.ArsnovaSocketioServerImpl;
import de.thm.arsnova.websocket.ArsnovaSocketioServerListener;
import de.thm.arsnova.web.CacheControlInterceptorHandler;
import de.thm.arsnova.web.CorsFilter;
import de.thm.arsnova.web.DeprecatedApiInterceptorHandler;
import de.thm.arsnova.web.ResponseInterceptorHandler;
import org.ektorp.CouchDbConnector;
import org.ektorp.impl.StdCouchDbInstance;
import org.ektorp.spring.HttpClientFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
......@@ -88,7 +80,6 @@ import java.util.List;
"de.thm.arsnova.aop",
"de.thm.arsnova.cache",
"de.thm.arsnova.controller",
"de.thm.arsnova.domain",
"de.thm.arsnova.dao",
"de.thm.arsnova.events",
"de.thm.arsnova.security",
......@@ -261,19 +252,19 @@ public class AppConfig extends WebMvcConfigurerAdapter {
@Profile("!test")
@Bean(name = "socketServer", initMethod = "startServer", destroyMethod = "stopServer")
public ARSnovaSocket socketServer() {
final ARSnovaSocketIOServer socketServer = new ARSnovaSocketIOServer();
socketServer.setHostIp(socketAddress);
socketServer.setPortNumber(socketPort);
socketServer.setUseSSL(!socketKeystore.isEmpty());
socketServer.setKeystore(socketKeystore);
socketServer.setStorepass(socketKeystorePassword);
return socketServer;
public ArsnovaSocketioServer socketServer() {
final ArsnovaSocketioServerImpl socketioServer = new ArsnovaSocketioServerImpl();
socketioServer.setHostIp(socketAddress);
socketioServer.setPortNumber(socketPort);
socketioServer.setUseSSL(!socketKeystore.isEmpty());
socketioServer.setKeystore(socketKeystore);
socketioServer.setStorepass(socketKeystorePassword);
return socketioServer;
}
@Bean
public ARSnovaSocketListener arsnovaSocketListener() {
return new ARSnovaSocketListener();
public ArsnovaSocketioServerListener arsnovaSocketListener() {
return new ArsnovaSocketioServerListener();
}
@Bean
......
......@@ -17,10 +17,10 @@
*/
package de.thm.arsnova.config;
import de.thm.arsnova.CASLogoutSuccessHandler;
import de.thm.arsnova.CasUserDetailsService;
import de.thm.arsnova.LoginAuthenticationFailureHandler;
import de.thm.arsnova.LoginAuthenticationSucessHandler;
import de.thm.arsnova.security.CasLogoutSuccessHandler;
import de.thm.arsnova.security.CasUserDetailsService;
import de.thm.arsnova.security.LoginAuthenticationFailureHandler;
import de.thm.arsnova.security.LoginAuthenticationSucessHandler;
import de.thm.arsnova.security.ApplicationPermissionEvaluator;
import de.thm.arsnova.security.CustomLdapUserDetailsMapper;
import de.thm.arsnova.security.DbUserDetailsService;
......@@ -364,7 +364,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public LogoutSuccessHandler casLogoutSuccessHandler() {
CASLogoutSuccessHandler handler = new CASLogoutSuccessHandler();
CasLogoutSuccessHandler handler = new CasLogoutSuccessHandler();
handler.setCasUrl(casUrl);
handler.setDefaultTarget(rootUrl);
......
......@@ -17,10 +17,10 @@
*/
package de.thm.arsnova.controller;
import de.thm.arsnova.entities.Comment;
import de.thm.arsnova.entities.CommentReadingCount;
import de.thm.arsnova.entities.transport.Comment;
import de.thm.arsnova.exceptions.BadRequestException;
import de.thm.arsnova.services.IContentService;
import de.thm.arsnova.services.CommentService;
import de.thm.arsnova.web.DeprecatedApi;
import de.thm.arsnova.web.Pagination;
import io.swagger.annotations.Api;
......@@ -49,7 +49,7 @@ import java.util.List;
public class CommentController extends PaginationController {
@Autowired
private IContentService contentService;
private CommentService commentService;
@ApiOperation(value = "Count all the comments in current session",
nickname = "getAudienceQuestionCount")
......@@ -57,7 +57,7 @@ public class CommentController extends PaginationController {
@DeprecatedApi
@Deprecated
public int getInterposedCount(@ApiParam(value = "Session-Key from current session", required = true) @RequestParam final String sessionkey) {
return contentService.getInterposedCount(sessionkey);
return commentService.count(sessionkey);
}
@ApiOperation(value = "count all unread comments",
......@@ -66,7 +66,7 @@ public class CommentController extends PaginationController {
@DeprecatedApi
@Deprecated
public CommentReadingCount getUnreadInterposedCount(@ApiParam(value = "Session-Key from current session", required = true) @RequestParam("sessionkey") final String sessionkey, String user) {
return contentService.getInterposedReadingCount(sessionkey, user);
return commentService.countRead(sessionkey, user);
}
@ApiOperation(value = "Retrieves all Comments for a Session",
......@@ -74,14 +74,14 @@ public class CommentController extends PaginationController {
@RequestMapping(value = "/", method = RequestMethod.GET)
@Pagination
public List<Comment> getInterposedQuestions(@ApiParam(value = "Session-Key from current session", required = true) @RequestParam final String sessionkey) {
return Comment.fromList(contentService.getInterposedQuestions(sessionkey, offset, limit));
return commentService.getBySessionKey(sessionkey, offset, limit);
}
@ApiOperation(value = "Retrieves an Comment",
nickname = "getInterposedQuestion")
@RequestMapping(value = "/{questionId}", method = RequestMethod.GET)
public Comment getInterposedQuestion(@ApiParam(value = "ID of the Comment that needs to be deleted", required = true) @PathVariable final String questionId) {
return new Comment(contentService.readInterposedQuestion(questionId));
return commentService.getAndMarkRead(questionId);
}
@ApiOperation(value = "Creates a new Comment for a Session and returns the Comment's data",
......@@ -95,7 +95,7 @@ public class CommentController extends PaginationController {
@ApiParam(value = "Session-Key from current session", required = true) @RequestParam final String sessionkey,
@ApiParam(value = "the body from the new comment", required = true) @RequestBody final de.thm.arsnova.entities.Comment comment
) {
if (contentService.saveQuestion(comment)) {
if (commentService.save(comment)) {
return;
}
......@@ -106,6 +106,6 @@ public class CommentController extends PaginationController {
nickname = "deleteInterposedQuestion")
@RequestMapping(value = "/{questionId}", method = RequestMethod.DELETE)
public void deleteInterposedQuestion(@ApiParam(value = "ID of the comment that needs to be deleted", required = true) @PathVariable final String questionId) {
contentService.deleteInterposedQuestion(questionId);
commentService.delete(questionId);
}
}
......@@ -17,14 +17,14 @@
*/
package de.thm.arsnova.controller;
import de.thm.arsnova.PaginationListDecorator;
import de.thm.arsnova.util.PaginationListDecorator;
import de.thm.arsnova.entities.Answer;
import de.thm.arsnova.entities.Content;
import de.thm.arsnova.exceptions.BadRequestException;
import de.thm.arsnova.exceptions.ForbiddenException;
import de.thm.arsnova.exceptions.NoContentException;
import de.thm.arsnova.exceptions.NotFoundException;
import de.thm.arsnova.services.IContentService;
import de.thm.arsnova.services.ContentService;
import de.thm.arsnova.web.DeprecatedApi;
import de.thm.arsnova.web.Pagination;
import io.swagger.annotations.Api;
......@@ -54,7 +54,7 @@ import java.util.List;
@Api(value = "/lecturerquestion", description = "Operations for Lecture Questions")
public class ContentController extends PaginationController {
@Autowired
private IContentService contentService;
private ContentService contentService;
@ApiOperation(value = "Get question with provided question Id",
nickname = "getQuestion")
......@@ -63,7 +63,7 @@ public class ContentController extends PaginationController {
})
@RequestMapping(value = "/{questionId}", method = RequestMethod.GET)
public Content getQuestion(@PathVariable final String questionId) {
final Content content = contentService.getQuestion(questionId);
final Content content = contentService.get(questionId);
if (content != null) {
return content;
}
......@@ -79,7 +79,7 @@ public class ContentController extends PaginationController {
@RequestMapping(value = "/", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public Content postQuestion(@RequestBody final Content content) {
if (contentService.saveQuestion(content) != null) {
if (contentService.save(content) != null) {
return content;
}
throw new BadRequestException();
......@@ -93,7 +93,7 @@ public class ContentController extends PaginationController {
@ResponseStatus(HttpStatus.CREATED)
public List<Content> bulkPostQuestions(@RequestBody final List<Content> contents) {
for (final Content content : contents) {
if (contentService.saveQuestion(content) == null) {
if (contentService.save(content) == null) {
throw new BadRequestException();
}
}
......@@ -291,7 +291,7 @@ public class ContentController extends PaginationController {
} else if (preparationQuestionsOnly) {
contents = contentService.getPreparationQuestions(sessionkey);
} else {
contents = contentService.getSkillQuestions(sessionkey);
contents = contentService.getBySessionKey(sessionkey);
}
if (contents == null || contents.isEmpty()) {
response.setStatus(HttpStatus.NO_CONTENT.value());
......@@ -315,12 +315,12 @@ public class ContentController extends PaginationController {
) {
if (lectureQuestionsOnly) {
contentService.deleteLectureQuestions(sessionkey);
} else if (flashcardsOnly) {
contentService.deleteFlashcards(sessionkey);
} else if (preparationQuestionsOnly) {
contentService.deletePreparationQuestions(sessionkey);
} else if (flashcardsOnly) {
contentService.deleteFlashcards(sessionkey);
} else {
contentService.deleteAllQuestions(sessionkey);
contentService.deleteAllContent(sessionkey);
}
}
......@@ -336,13 +336,13 @@ public class ContentController extends PaginationController {
@RequestParam(value = "preparationquestionsonly", defaultValue = "false") final boolean preparationQuestionsOnly
) {
if (lectureQuestionsOnly) {
return contentService.getLectureQuestionCount(sessionkey);
} else if (flashcardsOnly) {
return contentService.getFlashcardCount(sessionkey);
return contentService.countLectureQuestions(sessionkey);
} else if (preparationQuestionsOnly) {
return contentService.getPreparationQuestionCount(sessionkey);
return contentService.countPreparationQuestions(sessionkey);
} else if (flashcardsOnly) {
return contentService.countFlashcards(sessionkey);
} else {
return contentService.getSkillQuestionCount