diff --git a/src/main/java/org/olat/ims/qti21/QTI21Service.java b/src/main/java/org/olat/ims/qti21/QTI21Service.java index a5c6ba23d8de5405fddbd9ec81e176cfc23c65c2..2cdea6e1e4e9305ed61cbdbd1512157a4f1665f8 100644 --- a/src/main/java/org/olat/ims/qti21/QTI21Service.java +++ b/src/main/java/org/olat/ims/qti21/QTI21Service.java @@ -95,6 +95,17 @@ public interface QTI21Service { public URI createAssessmentTestUri(File resourceDirectory); + /** + * Ensure the assessment test is cached and not expired by + * the max. idle configuration. The goal is to maintain the + * object in cache despite it to be strong reference by the + * run controller. + * + * @param resourceDirectory The directory where is the package + */ + public void touchCachedResolveAssessmentTest(File resourceDirectory); + + /** * Load the assessmentTest based on the imsmanifest.xml found in the resource * directory. Return null if the imsmanifest.xml is not found. The assessmentTest diff --git a/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java b/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java index cb01875501c63407cfc2fb5d4f762ddeed1f3e24..01e479399abc4d5851361dc9a41f5bf6ecaf2aa3 100644 --- a/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java +++ b/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java @@ -329,6 +329,14 @@ public class QTI21ServiceImpl implements QTI21Service, UserDataDeletable, Initia return testSessionDao.hasActiveTestSession(testEntry); } + @Override + public void touchCachedResolveAssessmentTest(File resourceDirectory) { + URI assessmentObjectSystemId = createAssessmentTestUri(resourceDirectory); + if(assessmentObjectSystemId != null) { + assessmentTestsCache.get(resourceDirectory); + } + } + @Override public ResolvedAssessmentTest loadAndResolveAssessmentTest(File resourceDirectory, boolean replace, boolean debugInfo) { URI assessmentObjectSystemId = createAssessmentTestUri(resourceDirectory); @@ -416,7 +424,7 @@ public class QTI21ServiceImpl implements QTI21Service, UserDataDeletable, Initia public URI createAssessmentTestUri(final File resourceDirectory) { final String key = resourceDirectory.getAbsolutePath(); try { - return resourceToTestURI.computeIfAbsent(key, (directoryAbsolutPath) -> { + return resourceToTestURI.computeIfAbsent(key, directoryAbsolutPath -> { File manifestPath = new File(resourceDirectory, "imsmanifest.xml"); QTI21ContentPackage cp = new QTI21ContentPackage(manifestPath.toPath()); try { diff --git a/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java b/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java index 125c03a5f2d11cf4a609897845e3c3dde838fa6f..d0aea891e5c466122ca8bd123c3fe2e1e612811a 100644 --- a/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java +++ b/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java @@ -178,6 +178,7 @@ public class AssessmentTestDisplayController extends BasicController implements private DialogBoxController confirmSuspendDialog; private CandidateEvent lastEvent; + private Date touchTimestamp; private Date currentRequestTimestamp; private AssessmentTestSession candidateSession; private ResolvedAssessmentTest resolvedAssessmentTest; @@ -255,6 +256,7 @@ public class AssessmentTestDisplayController extends BasicController implements FileResourceManager frm = FileResourceManager.getInstance(); fUnzippedDirRoot = frm.unzipFileResource(testEntry.getOlatResource()); resolvedAssessmentTest = qtiService.loadAndResolveAssessmentTest(fUnzippedDirRoot, false, false); + touchTimestamp = ureq.getRequestTimestamp(); if(resolvedAssessmentTest == null || resolvedAssessmentTest.getRootNodeLookup().extractIfSuccessful() == null) { mainVC = createVelocityContainer("error"); } else { @@ -630,6 +632,28 @@ public class AssessmentTestDisplayController extends BasicController implements return sessionDeleted; } + /** + * This method maintains the assessment test in cache during + * a test session. This controller doesn't need the cache, the + * test is strong referenced but at the end of the test, score + * evaluation of the course, rendering of the assessment result + * can need the cache again. For very large tests, it can be a + * performance issue. + * + * @param ureq The user request + */ + private void touchResolvedAssessmentTest(UserRequest ureq) { + try { + Date timestamp = ureq.getRequestTimestamp(); + if(touchTimestamp == null || (timestamp.getTime() > touchTimestamp.getTime() + 300000l)) { + touchTimestamp = timestamp; + qtiService.touchCachedResolveAssessmentTest(fUnzippedDirRoot); + } + } catch (Exception e) { + logError("", e); + } + } + private boolean timeLimitBarrier(UserRequest ureq) { Long assessmentTestMaxTimeLimits = getAssessmentTestMaxTimeLimit(); if(assessmentTestMaxTimeLimits != null) { @@ -796,6 +820,8 @@ public class AssessmentTestDisplayController extends BasicController implements restartTest(ureq); break; } + + touchResolvedAssessmentTest(ureq); } private void restartTest(UserRequest ureq) { diff --git a/src/main/java/org/olat/modules/adobeconnect/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/adobeconnect/ui/_i18n/LocalStrings_de.properties index aef5b7ba35ea5bf9e3cde03c2c86892b0990828f..fc50df9ab62ee6b86cd7c10186728bf6e9589919 100644 --- a/src/main/java/org/olat/modules/adobeconnect/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/modules/adobeconnect/ui/_i18n/LocalStrings_de.properties @@ -11,7 +11,7 @@ adobeconnect.module.enabled.for.groups=Gruppen adobeconnect.module.provider=Provider adobeconnect.title=Adobe Connect check=Serververbindung testen -confirm.delete.meeting=Wollen Sie wirklich den Meeting "{0}" l\u00F6schen? +confirm.delete.meeting=Wollen Sie wirklich das Meeting "{0}" l\u00F6schen? confirm.delete.meeting.title=Meeting "{0}" l\u00F6schen connection.failed=Login fehlgeschlagen. connection.successful=Login erfolgreich\! @@ -86,6 +86,6 @@ option.single.meeting=Verteilte Meetings option.single.meeting.single=Erstellt nur ein Meeting Raum pro Kursbaustein oder Gruppe option.single.meeting.perdate=Erstellt ein Meeting Raum pro Datum table.header.permanent=Dauernd -warning.no.access=Sie k\u00F6nnen noch nicht den Meeting beitreten. +warning.no.access=Sie k\u00F6nnen noch nicht dem Meeting beitreten. warning.no.meeting=Das Meeting wurde gel\u00F6scht. warning.not.registered.shared.documents=Nur die Personen die sich an den Meeting angemeldet haben d\u00FCrfen die Dokumenten ansehen. diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_de.properties index 36220af0adf6bfe6f0f1c8cd580a30f347ad52ee..f924fba5c1438fc2bca8549642ae1a5308204229 100644 --- a/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_de.properties @@ -187,7 +187,7 @@ view.template=Raumvorlage "{0}" warning.at.least.one.meeting=Sie m\u00FCssen mindestens ein Meeting w\u00E4hlen. warning.meeting.permission.denied=Sie haben nicht die Berechtigung den Meeting beizutreten. warning.meeting.started=Sie k\u00F6nnen ein gestartetes Meeting nicht mehr bearbeiten. -warning.no.access=Sie k\u00F6nnen noch nicht den Meeting beitreten. +warning.no.access=Sie k\u00F6nnen noch nicht dem Meeting beitreten. warning.no.meeting=Das Meeting wurde gel\u00F6scht. warning.template.in.use=Die Raumvorlage kann nicht gel\u00F6scht werden da sie nocht benutzt wird. L\u00F6schschen Sie zuerst die entsprechenden Online-Termine oder deaktivieren Sie die Raumvorlage. wizard.dates.title=Datum diff --git a/src/main/resources/infinispan-config.xml b/src/main/resources/infinispan-config.xml index c8232ad6fce4af10d22904aa4b6026393e6cbe71..b0eeb1a549353eb6801e3823d229791bac0087ed 100644 --- a/src/main/resources/infinispan-config.xml +++ b/src/main/resources/infinispan-config.xml @@ -63,6 +63,24 @@ <expiration max-idle="180000" interval="15000" /> </local-cache> + <local-cache name="QTIWorks@assessmentTests" simple-cache="true" statistics="true" statistics-available="true"> + <locking isolation="READ_COMMITTED" concurrency-level="1000" acquire-timeout="15000" striping="false" /> + <transaction mode="NONE" auto-commit="true" /> + <memory> + <object size="200" strategy="REMOVE" /> + </memory> + <expiration max-idle="900000" interval="15000" /> + </local-cache> + + <local-cache name="QTIWorks@xsltStylesheets" simple-cache="true" statistics="true" statistics-available="true"> + <locking isolation="READ_COMMITTED" concurrency-level="1000" acquire-timeout="15000" striping="false" /> + <transaction mode="NONE" auto-commit="true" /> + <memory> + <object size="200" strategy="REMOVE" /> + </memory> + <expiration max-idle="1800000" interval="15000" /> + </local-cache> + <local-cache name="WebDAVManager@webdav" simple-cache="true" statistics="true" statistics-available="true"> <locking isolation="READ_COMMITTED" concurrency-level="1000" acquire-timeout="15000" striping="false" /> <transaction mode="NONE" auto-commit="true" />