diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java index 0634bc50..3dc88fe7 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java @@ -733,7 +733,9 @@ public class ExamDAOImpl implements ExamDAO { log.debug("Quizzes size mismatch detected by getting exams quiz data from LMS: {}", lmsSetup); } - if (lmsSetup.testCourseAccessAPI().hasAnyError()) { + try { + lmsSetup.checkCourseAPIAccess(); + } catch (final Exception e) { // No course access on the LMS. This means we can't get any quizzes from this LMSSetup at the moment // All exams are marked as corrupt because of LMS Setup failure diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/CourseAccessAPI.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/CourseAccessAPI.java index 455ffdd7..026c23e5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/CourseAccessAPI.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/CourseAccessAPI.java @@ -42,6 +42,14 @@ public interface CourseAccessAPI { * @return {@link LmsSetupTestResult } instance with the test result report */ LmsSetupTestResult testCourseAccessAPI(); + /** To make a quick course API access check without report that just throws an exception if not available */ + default void checkCourseAPIAccess() { + final LmsSetupTestResult testCourseAccessAPI = this.testCourseAccessAPI(); + if (!testCourseAccessAPI.isOk()) { + throw new RuntimeException("No course API Access: " + testCourseAccessAPI); + } + } + /** Get an unsorted List of filtered {@link QuizData } from the LMS course/quiz API * * @param filterMap the {@link FilterMap } to get a filtered result. Possible filter attributes are: diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPITemplateAdapter.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPITemplateAdapter.java index 365c9452..d5ac9019 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPITemplateAdapter.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPITemplateAdapter.java @@ -43,6 +43,8 @@ public class LmsAPITemplateAdapter implements LmsAPITemplate { private final SEBRestrictionAPI sebBestrictionAPI; private final APITemplateDataSupplier apiTemplateDataSupplier; + /** CircuitBreaker for protected lmsTestRequest */ + private final CircuitBreaker lmsTestRequest; /** CircuitBreaker for protected quiz and course data requests */ private final CircuitBreaker> allQuizzesRequest; /** CircuitBreaker for protected quiz and course data requests */ @@ -68,15 +70,29 @@ public class LmsAPITemplateAdapter implements LmsAPITemplate { this.sebBestrictionAPI = sebBestrictionAPI; this.apiTemplateDataSupplier = apiTemplateDataSupplier; + this.lmsTestRequest = asyncService.createCircuitBreaker( + environment.getProperty( + "sebserver.webservice.circuitbreaker.lmsTestRequest.attempts", + Integer.class, + 2), + environment.getProperty( + "sebserver.webservice.circuitbreaker.lmsTestRequest.blockingTime", + Long.class, + Constants.SECOND_IN_MILLIS * 20), + environment.getProperty( + "sebserver.webservice.circuitbreaker.lmsTestRequest.timeToRecover", + Long.class, + Constants.MINUTE_IN_MILLIS)); + this.allQuizzesRequest = asyncService.createCircuitBreaker( environment.getProperty( "sebserver.webservice.circuitbreaker.quizzesRequest.attempts", Integer.class, - 3), + 1), environment.getProperty( "sebserver.webservice.circuitbreaker.quizzesRequest.blockingTime", Long.class, - Constants.MINUTE_IN_MILLIS), + Constants.SECOND_IN_MILLIS * 20), environment.getProperty( "sebserver.webservice.circuitbreaker.quizzesRequest.timeToRecover", Long.class, @@ -86,7 +102,7 @@ public class LmsAPITemplateAdapter implements LmsAPITemplate { environment.getProperty( "sebserver.webservice.circuitbreaker.quizzesRequest.attempts", Integer.class, - 3), + 1), environment.getProperty( "sebserver.webservice.circuitbreaker.quizzesRequest.blockingTime", Long.class, @@ -100,7 +116,7 @@ public class LmsAPITemplateAdapter implements LmsAPITemplate { environment.getProperty( "sebserver.webservice.circuitbreaker.quizzesRequest.attempts", Integer.class, - 3), + 1), environment.getProperty( "sebserver.webservice.circuitbreaker.quizzesRequest.blockingTime", Long.class, @@ -177,14 +193,30 @@ public class LmsAPITemplateAdapter implements LmsAPITemplate { return this.apiTemplateDataSupplier.getLmsSetup(); } + @Override + public void checkCourseAPIAccess() { + this.lmsTestRequest + .protectedRun(() -> { + final LmsSetupTestResult testCourseAccessAPI = this.courseAccessAPI.testCourseAccessAPI(); + if (!testCourseAccessAPI.isOk()) { + throw new RuntimeException("No course API Access: " + testCourseAccessAPI); + } + return testCourseAccessAPI; + }); + } + @Override public LmsSetupTestResult testCourseAccessAPI() { if (this.courseAccessAPI != null) { - return this.courseAccessAPI.testCourseAccessAPI(); - } + if (log.isDebugEnabled()) { + log.debug("Test Course Access API for LMSSetup: {}", lmsSetup()); + } - if (log.isDebugEnabled()) { - log.debug("Test Course Access API for LMSSetup: {}", lmsSetup()); + return this.lmsTestRequest.protectedRun(() -> this.courseAccessAPI.testCourseAccessAPI()) + .onError(error -> log.error( + "Failed to run protectedQuizzesRequest: {}", + error.getMessage())) + .getOrThrow(); } return LmsSetupTestResult.ofAPINotSupported(getType()); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxCourseAccess.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxCourseAccess.java index acce20b6..57d2f0a3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxCourseAccess.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxCourseAccess.java @@ -152,10 +152,12 @@ final class OpenEdxCourseAccess extends AbstractCachedCourseAccess implements Co final LmsSetup lmsSetup = getApiTemplateDataSupplier().getLmsSetup(); final String externalStartURI = getExternalLMSServerAddress(lmsSetup); - final QuizData quizData = quizDataOf( - lmsSetup, - this.getOneCourse(id, this.restTemplate, id), - externalStartURI); + final QuizData quizData = getRestTemplate() + .map(template -> quizDataOf( + lmsSetup, + this.getOneCourse(id, template, id), + externalStartURI)) + .getOrThrow(); if (quizData != null) { super.putToCache(quizData); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionCacheService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionCacheService.java index 3d673263..c73714a9 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionCacheService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionCacheService.java @@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl; import java.io.ByteArrayOutputStream; +import org.apache.commons.lang3.BooleanUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.annotation.CacheEvict; @@ -114,7 +115,7 @@ public class ExamSessionCacheService { } public boolean isRunning(final Exam exam) { - if (exam == null || !exam.active) { + if (exam == null || !exam.active || BooleanUtils.isFalse(exam.lmsDataAvailable)) { return false; }