diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/async/CircuitBreaker.java b/src/main/java/ch/ethz/seb/sebserver/gbl/async/CircuitBreaker.java index e1c95b0c..61d85d65 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/async/CircuitBreaker.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/async/CircuitBreaker.java @@ -87,7 +87,7 @@ public final class CircuitBreaker { /** Create new CircuitBreakerSupplier. * * @param asyncRunner the AsyncRunner used to create asynchronous calls on the given supplier function - * @param maxFailingAttempts the number of maximal failing attempts before go form CLOSE into HALF_OPEN state + * @param maxFailingAttempts the number of maximal failing attempts before go from CLOSE into HALF_OPEN state * @param maxBlockingTime the maximal time that an call attempt can block until an error is responded * @param timeToRecover the time the circuit breaker needs to cool-down on OPEN-STATE before going back to HALF_OPEN * state */ @@ -169,7 +169,7 @@ public final class CircuitBreaker { final long currentBlockingTime = Utils.getMillisecondsNow() - startTime; final int failing = this.failingCount.incrementAndGet(); - if (failing > this.maxFailingAttempts || currentBlockingTime > this.maxBlockingTime) { + if (failing >= this.maxFailingAttempts || currentBlockingTime > this.maxBlockingTime) { // brake thought to HALF_OPEN state and return error if (log.isDebugEnabled()) { log.debug("Changing state from Open to Half Open"); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java index 50cb3915..33fc6c3f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java @@ -445,21 +445,21 @@ public class ExamForm implements TemplateComposer { .withEntityKey(entityKey) .withExec(this.examSEBRestrictionSettings.settingsFunction(this.pageService)) .withAttribute(ExamSEBRestrictionSettings.PAGE_CONTEXT_ATTR_LMS_ID, String.valueOf(exam.lmsSetupId)) - .withAttribute(PageContext.AttributeKeys.FORCE_READ_ONLY, String.valueOf(!modifyGrant)) + .withAttribute(PageContext.AttributeKeys.FORCE_READ_ONLY, String.valueOf(!modifyGrant || !editable)) .noEventPropagation() .publishIf(() -> sebRestrictionAvailable && readonly) .newAction(ActionDefinition.EXAM_ENABLE_SEB_RESTRICTION) .withEntityKey(entityKey) .withExec(action -> this.examSEBRestrictionSettings.setSEBRestriction(action, true, this.restService)) - .publishIf(() -> sebRestrictionAvailable && readonly && editable && !importFromQuizData + .publishIf(() -> sebRestrictionAvailable && readonly && modifyGrant && !importFromQuizData && BooleanUtils.isFalse(isRestricted)) .newAction(ActionDefinition.EXAM_DISABLE_SEB_RESTRICTION) .withConfirm(() -> ACTION_MESSAGE_SEB_RESTRICTION_RELEASE) .withEntityKey(entityKey) .withExec(action -> this.examSEBRestrictionSettings.setSEBRestriction(action, false, this.restService)) - .publishIf(() -> sebRestrictionAvailable && readonly && editable && !importFromQuizData + .publishIf(() -> sebRestrictionAvailable && readonly && modifyGrant && !importFromQuizData && BooleanUtils.isTrue(isRestricted)) .newAction(ActionDefinition.EXAM_PROCTORING_ON) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java index c61dae30..5d4c30ad 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java @@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl; import static org.mybatis.dynamic.sql.SqlBuilder.*; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -64,6 +65,10 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler; @WebServiceProfile public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO { + private static final List ACTIVE_EXAM_STATE_NAMES = Arrays.asList( + ExamStatus.FINISHED.name(), + ExamStatus.ARCHIVED.name()); + private final ExamRecordMapper examRecordMapper; private final ExamConfigurationMapRecordMapper examConfigurationMapRecordMapper; private final ConfigurationNodeRecordMapper configurationNodeRecordMapper; @@ -344,7 +349,8 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO { @Override @Transactional(readOnly = true) public Result> getExamIdsForConfigNodeId(final Long configurationNodeId) { - return Result.tryCatch(() -> this.examConfigurationMapRecordMapper.selectByExample() + return Result.tryCatch(() -> this.examConfigurationMapRecordMapper + .selectByExample() .where( ExamConfigurationMapRecordDynamicSqlSupport.configurationNodeId, isEqualTo(configurationNodeId)) @@ -383,16 +389,16 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO { .build() .execute() .stream() - .filter(rec -> !isExamFinished(rec.getExamId())) + .filter(rec -> !isExamActive(rec.getExamId())) .findFirst() .isPresent()); } - private boolean isExamFinished(final Long examId) { + private boolean isExamActive(final Long examId) { try { return this.examRecordMapper.countByExample() .where(ExamRecordDynamicSqlSupport.id, isEqualTo(examId)) - .and(ExamRecordDynamicSqlSupport.status, isEqualTo(ExamStatus.FINISHED.name())) + .and(ExamRecordDynamicSqlSupport.status, isIn(ACTIVE_EXAM_STATE_NAMES)) .build() .execute() >= 1; } catch (final Exception e) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java index 347d383a..1ed5da11 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java @@ -97,6 +97,13 @@ public interface ExamAdminService { * @return ExamProctoringService instance */ Result getExamProctoringService(final Long examId); + /** This archives a finished exam and set it to archived state as well as the assigned + * exam configurations that are also set to archived state. + * + * @param exam The exam to archive + * @return Result refer to the archived exam or to an error when happened */ + Result archiveExam(Exam exam); + /** Used to check threshold consistency for a given list of thresholds. * Checks if all values are present (none null value) * Checks if there are duplicates diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java index ea143926..b424bbe2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java @@ -20,17 +20,24 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus; import ch.ethz.seb.sebserver.gbl.model.exam.OpenEdxSEBRestriction; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings; import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationStatus; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationNodeDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamConfigurationMapDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService; import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ProctoringAdminService; @@ -49,17 +56,23 @@ public class ExamAdminServiceImpl implements ExamAdminService { private final ExamDAO examDAO; private final ProctoringAdminService proctoringServiceSettingsService; private final AdditionalAttributesDAO additionalAttributesDAO; + private final ConfigurationNodeDAO configurationNodeDAO; + private final ExamConfigurationMapDAO examConfigurationMapDAO; private final LmsAPIService lmsAPIService; protected ExamAdminServiceImpl( final ExamDAO examDAO, final ProctoringAdminService proctoringServiceSettingsService, final AdditionalAttributesDAO additionalAttributesDAO, + final ConfigurationNodeDAO configurationNodeDAO, + final ExamConfigurationMapDAO examConfigurationMapDAO, final LmsAPIService lmsAPIService) { this.examDAO = examDAO; this.proctoringServiceSettingsService = proctoringServiceSettingsService; this.additionalAttributesDAO = additionalAttributesDAO; + this.configurationNodeDAO = configurationNodeDAO; + this.examConfigurationMapDAO = examConfigurationMapDAO; this.lmsAPIService = lmsAPIService; } @@ -114,7 +127,8 @@ public class ExamAdminServiceImpl implements ExamAdminService { return this.lmsAPIService .getLmsAPITemplate(exam.lmsSetupId) - .map(lmsAPI -> !lmsAPI.hasSEBClientRestriction(exam)); + .map(lmsAPI -> lmsAPI.hasSEBClientRestriction(exam)) + .onError(error -> log.error("Failed to check SEB restriction: ", error)); } @Override @@ -182,4 +196,55 @@ public class ExamAdminServiceImpl implements ExamAdminService { }); } + @Override + public Result archiveExam(final Exam exam) { + return Result.tryCatch(() -> { + + if (exam.status != ExamStatus.FINISHED) { + throw new APIMessageException( + APIMessage.ErrorMessage.INTEGRITY_VALIDATION.of("Exam is in wrong status to archive.")); + } + + if (log.isDebugEnabled()) { + log.debug("Archiving exam: {}", exam); + } + + if (this.isRestricted(exam).getOr(false)) { + + if (log.isDebugEnabled()) { + log.debug("Archiving exam, SEB restriction still active, try to release: {}", exam); + } + + this.lmsAPIService + .getLmsAPITemplate(exam.lmsSetupId) + .flatMap(lms -> lms.releaseSEBClientRestriction(exam)) + .onError(error -> log.error( + "Failed to release SEB client restriction for archiving exam: ", + error)); + } + + final Exam result = this.examDAO + .updateState(exam.id, ExamStatus.ARCHIVED, null) + .getOrThrow(); + + this.examConfigurationMapDAO + .getConfigurationNodeIds(result.id) + .getOrThrow() + .stream() + .forEach(configNodeId -> { + if (this.examConfigurationMapDAO.checkNoActiveExamReferences(configNodeId).getOr(false)) { + log.debug("Also set exam configuration to archived: ", configNodeId); + this.configurationNodeDAO.save( + new ConfigurationNode( + configNodeId, null, null, null, null, null, + null, ConfigurationStatus.ARCHIVED, null, null)) + .onError(error -> log.error("Failed to set exam configuration to archived state: ", + error)); + } + }); + + return result; + }); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/SEBRestrictionAPI.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/SEBRestrictionAPI.java index 870dc274..ada09e56 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/SEBRestrictionAPI.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/SEBRestrictionAPI.java @@ -37,7 +37,7 @@ public interface SEBRestrictionAPI { * * A SEB Restriction is available if there it can get from LMS and if there is either a Config-Key * or a BrowserExam-Key set or both. If none of this keys is set, the SEB Restriction is been - * considdered to not set on the LMS. + * considered to not set on the LMS. * * @param exam exam the exam to get the SEB restriction data for * @return true if there is a SEB restriction set on the LMS for the exam or false otherwise */ 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 c0846103..71f79253 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 @@ -130,7 +130,7 @@ public class LmsAPITemplateAdapter implements LmsAPITemplate { environment.getProperty( "sebserver.webservice.circuitbreaker.chaptersRequest.attempts", Integer.class, - 3), + 1), environment.getProperty( "sebserver.webservice.circuitbreaker.chaptersRequest.blockingTime", Long.class, @@ -158,7 +158,7 @@ public class LmsAPITemplateAdapter implements LmsAPITemplate { environment.getProperty( "sebserver.webservice.circuitbreaker.sebrestriction.attempts", Integer.class, - 2), + 1), environment.getProperty( "sebserver.webservice.circuitbreaker.sebrestriction.blockingTime", Long.class, @@ -388,20 +388,24 @@ public class LmsAPITemplateAdapter implements LmsAPITemplate { return this.restrictionRequest.protectedRun(() -> this.sebRestrictionAPI .getSEBClientRestriction(exam) - .onError(error -> log.error( - "Failed to get SEB restrictions: {}", - error.getMessage())) + .onError(error -> { + if (error instanceof NoSEBRestrictionException) { + return; + } + log.error("Failed to get SEB restrictions: {}", error.getMessage()); + }) .getOrThrow()); } @Override public boolean hasSEBClientRestriction(final Exam exam) { - if (this.sebRestrictionAPI == null) { + final Result sebClientRestriction = getSEBClientRestriction(exam); + if (sebClientRestriction.hasError()) { return false; } - return this.sebRestrictionAPI - .getSEBClientRestriction(exam).hasError(); + final SEBRestriction sebRestriction = sebClientRestriction.get(); + return !sebRestriction.configKeys.isEmpty() || !sebRestriction.browserExamKeys.isEmpty(); } @Override diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxCourseRestriction.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxCourseRestriction.java index 4e32bdcb..fceb011e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxCourseRestriction.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxCourseRestriction.java @@ -30,7 +30,6 @@ import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.SEBRestrictionAPI; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.NoSEBRestrictionException; /** The open edX SEB course restriction API implementation. * @@ -134,8 +133,9 @@ public class OpenEdxCourseRestriction implements SEBRestrictionAPI { } return SEBRestriction.from(exam.id, data); } catch (final HttpClientErrorException ce) { - if (ce.getStatusCode() == HttpStatus.NOT_FOUND || ce.getStatusCode() == HttpStatus.UNAUTHORIZED) { - throw new NoSEBRestrictionException(ce); + if (ce.getStatusCode() == HttpStatus.NOT_FOUND) { + // No SEB restriction is set for the specified exam, return an empty one + return new SEBRestriction(exam.id, null, null, null); } throw ce; } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java index cc4ee496..b910eb7c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java @@ -47,7 +47,6 @@ import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.model.PageSortOrder; import ch.ethz.seb.sebserver.gbl.model.exam.Chapters; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; -import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings; import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; import ch.ethz.seb.sebserver.gbl.model.exam.SEBRestriction; @@ -229,8 +228,9 @@ public class ExamAdministrationController extends EntityController { checkWritePrivilege(institutionId); return this.examDAO.byPK(modelId) .flatMap(this::checkWriteAccess) - .flatMap(this::checkArchive) - .flatMap(exam -> this.examDAO.updateState(exam.id, ExamStatus.ARCHIVED, null)) + .flatMap(this.examAdminService::archiveExam) +// .flatMap(this::checkArchive) +// .flatMap(exam -> this.examDAO.updateState(exam.id, ExamStatus.ARCHIVED, null)) .flatMap(this::logModify) .getOrThrow(); } @@ -581,15 +581,6 @@ public class ExamAdministrationController extends EntityController { }); } - private Result checkArchive(final Exam exam) { - if (exam.status != ExamStatus.FINISHED) { - throw new APIMessageException( - APIMessage.ErrorMessage.INTEGRITY_VALIDATION.of("Exam is in wrong status to archive.")); - } - - return Result.of(exam); - } - static Function, List> pageSort(final String sort) { final String sortBy = PageSortOrder.decode(sort); diff --git a/src/main/resources/config/application-dev-gui.properties b/src/main/resources/config/application-dev-gui.properties index a22c98bc..8069b264 100644 --- a/src/main/resources/config/application-dev-gui.properties +++ b/src/main/resources/config/application-dev-gui.properties @@ -1,11 +1,11 @@ server.address=localhost -server.port=8090 +server.port=8080 sebserver.gui.http.external.scheme=http sebserver.gui.entrypoint=/gui sebserver.gui.webservice.protocol=http sebserver.gui.webservice.address=localhost -sebserver.gui.webservice.port=8090 +sebserver.gui.webservice.port=8080 sebserver.gui.webservice.apipath=/admin-api/v1 # defines the polling interval that is used to poll the webservice for client connection data on a monitored exam page sebserver.gui.webservice.poll-interval=1000 diff --git a/src/test/java/ch/ethz/seb/sebserver/gbl/async/CircuitBreakerTest.java b/src/test/java/ch/ethz/seb/sebserver/gbl/async/CircuitBreakerTest.java index d57f65bb..62769d00 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gbl/async/CircuitBreakerTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/gbl/async/CircuitBreakerTest.java @@ -39,10 +39,61 @@ public class CircuitBreakerTest { assertNotNull(this.asyncService); } + @Test + public void testMaxAttempts1() { + final CircuitBreaker circuitBreaker = + this.asyncService.createCircuitBreaker(1, 500, 1000); + + final AtomicInteger attemptCounter = new AtomicInteger(0); + final Supplier tester = () -> { + attemptCounter.getAndIncrement(); + throw new RuntimeException("Test Error"); + }; + + final Result result = circuitBreaker.protectedRun(tester); // 1. call... + assertTrue(result.hasError()); + assertEquals(State.HALF_OPEN, circuitBreaker.getState()); + assertEquals("1", String.valueOf(attemptCounter.get())); + } + + @Test + public void testMaxAttempts4() { + final CircuitBreaker circuitBreaker = + this.asyncService.createCircuitBreaker(4, 500, 1000); + + final AtomicInteger attemptCounter = new AtomicInteger(0); + final Supplier tester = () -> { + attemptCounter.getAndIncrement(); + throw new RuntimeException("Test Error"); + }; + + final Result result = circuitBreaker.protectedRun(tester); // 1. call... + assertTrue(result.hasError()); + assertEquals(State.HALF_OPEN, circuitBreaker.getState()); + assertEquals("4", String.valueOf(attemptCounter.get())); + } + + @Test + public void testMaxAttempts0() { + final CircuitBreaker circuitBreaker = + this.asyncService.createCircuitBreaker(0, 500, 1000); + + final AtomicInteger attemptCounter = new AtomicInteger(0); + final Supplier tester = () -> { + attemptCounter.getAndIncrement(); + throw new RuntimeException("Test Error"); + }; + + final Result result = circuitBreaker.protectedRun(tester); // 1. call... + assertTrue(result.hasError()); + assertEquals(State.HALF_OPEN, circuitBreaker.getState()); + assertEquals("1", String.valueOf(attemptCounter.get())); + } + @Test public void roundtrip1() throws InterruptedException { final CircuitBreaker circuitBreaker = - this.asyncService.createCircuitBreaker(3, 500, 1000); + this.asyncService.createCircuitBreaker(4, 500, 1000); final Supplier tester = tester(100, 5, 10); diff --git a/src/test/java/ch/ethz/seb/sebserver/gbl/async/MemoizingCircuitBreakerTest.java b/src/test/java/ch/ethz/seb/sebserver/gbl/async/MemoizingCircuitBreakerTest.java index 6b96f907..c7990b7a 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gbl/async/MemoizingCircuitBreakerTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/gbl/async/MemoizingCircuitBreakerTest.java @@ -42,7 +42,7 @@ public class MemoizingCircuitBreakerTest { @Test public void roundtrip1() throws InterruptedException { final MemoizingCircuitBreaker circuitBreaker = this.asyncService.createMemoizingCircuitBreaker( - tester(100, 5, 10), 3, 500, 1000, true, 1000); + tester(100, 5, 10), 4, 500, 1000, true, 1000); assertNull(circuitBreaker.getCached());