Skip to content

Commit 2a38e74

Browse files
committed
Merge remote-tracking branch 'origin/dev-1.2'
2 parents aaa7679 + aad1a37 commit 2a38e74

26 files changed

+855
-255
lines changed

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<packaging>jar</packaging>
1717

1818
<properties>
19-
<seb-sps-version>1.2.1-SNAPSHOT</seb-sps-version>
19+
<seb-sps-version>1.2.1</seb-sps-version>
2020
<build-version>${seb-sps-version}</build-version>
2121
<revision>${seb-sps-version}</revision>
2222
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -189,7 +189,7 @@
189189
<dependency>
190190
<groupId>commons-io</groupId>
191191
<artifactId>commons-io</artifactId>
192-
<version>[2.14.0,)</version>
192+
<version>2.14.0</version>
193193
</dependency>
194194

195195
<dependency>

src/main/java/ch/ethz/seb/sps/server/ServiceUpdateTask.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import static ch.ethz.seb.sps.server.ServiceConfig.SYSTEM_SCHEDULER;
1212

13+
import ch.ethz.seb.sps.server.servicelayer.LiveProctoringCacheService;
1314
import org.springframework.beans.factory.DisposableBean;
1415
import org.springframework.beans.factory.annotation.Value;
1516
import org.springframework.context.annotation.Lazy;
@@ -22,13 +23,16 @@
2223
public class ServiceUpdateTask implements DisposableBean {
2324

2425
private final ServiceInfo serviceInfo;
26+
private final LiveProctoringCacheService liveProctoringCacheService;
2527
private final long updateInterval;
2628

2729
public ServiceUpdateTask(
28-
final ServiceInfo serviceInfo,
30+
final ServiceInfo serviceInfo,
31+
final LiveProctoringCacheService liveProctoringCacheService,
2932
@Value("${sps.webservice.distributed.update:15000}") final long updateInterval) {
3033

3134
this.serviceInfo = serviceInfo;
35+
this.liveProctoringCacheService = liveProctoringCacheService;
3236
this.updateInterval = updateInterval;
3337
}
3438

@@ -51,8 +55,10 @@ private void init() {
5155
fixedDelayString = "${sps.webservice.distributed.update:15000}",
5256
initialDelay = 5000,
5357
scheduler = SYSTEM_SCHEDULER)
54-
private void examSessionUpdateTask() {
58+
private void sessionUpdateTask() {
5559
this.serviceInfo.updateMaster();
60+
61+
liveProctoringCacheService.cleanup(this.serviceInfo.isMaster());
5662
}
5763

5864
@Override

src/main/java/ch/ethz/seb/sps/server/datalayer/dao/ScreenshotDataDAO.java

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,38 +9,29 @@
99
package ch.ethz.seb.sps.server.datalayer.dao;
1010

1111
import java.sql.Date;
12-
import java.util.Arrays;
1312
import java.util.Collection;
1413
import java.util.List;
1514
import java.util.Map;
1615

1716
import ch.ethz.seb.sps.domain.model.PageSortOrder;
1817
import ch.ethz.seb.sps.domain.model.service.UserListForApplicationSearch;
19-
import org.apache.commons.lang3.StringUtils;
20-
import org.springframework.transaction.annotation.Transactional;
2118

2219
import ch.ethz.seb.sps.domain.model.FilterMap;
2320
import ch.ethz.seb.sps.domain.model.service.ScreenshotData;
2421
import ch.ethz.seb.sps.domain.model.service.Session.ImageFormat;
2522
import ch.ethz.seb.sps.server.datalayer.batis.model.ScreenshotDataRecord;
26-
import ch.ethz.seb.sps.utils.Constants;
2723
import ch.ethz.seb.sps.utils.Result;
2824

2925
public interface ScreenshotDataDAO extends EntityDAO<ScreenshotData, ScreenshotData> {
3026

31-
Result<Collection<ScreenshotData>> allOfSession(String sessionUUID);
32-
33-
Result<ScreenshotDataRecord> getAt(String sessionUUID, Long at);
34-
35-
Result<Long> getIdAt(String sessionUUID, Long at);
36-
37-
Result<Long> getLatestImageId(String sessionUUID);
27+
Result<Collection<ScreenshotDataRecord>> allOfSession(String sessionUUID);
3828

3929
Result<Collection<Long>> getScreenshotTimestamps(String sessionUUID, Long timestamp, PageSortOrder sortOrder);
4030

31+
@Deprecated
4132
Result<ScreenshotDataRecord> getLatest(String sessionUUID);
4233

43-
Result<Map<String, ScreenshotDataRecord>> allLatestIn(List<String> sessionUUIDs);
34+
Result<Map<String, ScreenshotDataRecord>> allOfMappedToSession(List<Long> pks);
4435

4536
Result<Long> save(
4637
String sessionId,
@@ -62,4 +53,5 @@ Result<Long> save(
6253

6354
Result<List<Long>> getTimestampListForApplicationSearch(String sessionUuid, String metadataApplication, String metadataWindowTitle);
6455

56+
Result<ScreenshotDataRecord> recordByPK(Long latestSSDataId);
6557
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright (c) 2025 ETH Zürich, IT Services
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
7+
*/
8+
9+
package ch.ethz.seb.sps.server.datalayer.dao;
10+
11+
import java.util.Collection;
12+
import java.util.List;
13+
14+
import ch.ethz.seb.sps.server.datalayer.batis.model.ScreenshotDataLiveCacheRecord;
15+
import ch.ethz.seb.sps.utils.Result;
16+
17+
public interface ScreenshotDataLiveCacheDAO {
18+
19+
Result<ScreenshotDataLiveCacheRecord> createCacheEntry(String sessionUUID);
20+
21+
Result<ScreenshotDataLiveCacheRecord> createCacheEntry(String sessionUUID, Long value);
22+
23+
Result<String> deleteCacheEntry(String sessionUUID);
24+
25+
Result<List<String>> deleteAll(List<String> sessionUUIDs);
26+
27+
Result<Collection<ScreenshotDataLiveCacheRecord>> getAll();
28+
}
29+

src/main/java/ch/ethz/seb/sps/server/datalayer/dao/SessionDAO.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import java.util.Collection;
1313
import java.util.List;
1414
import java.util.Map;
15+
import java.util.Set;
1516

1617
import ch.ethz.seb.sps.domain.model.EntityKey;
1718
import ch.ethz.seb.sps.domain.model.FilterMap;
@@ -135,4 +136,10 @@ Result<Session> createNew(
135136
* @param termination_time The timestamp on which to close the session (unix timestamp im milliseconds)
136137
* @return Result refer to the close timestamp or to an error when happened.*/
137138
Result<Long> closeAt(String sessionUUID, Long termination_time);
139+
140+
/** Reduces the given list of sessionUUIDs to a list of session UUIDs of all closed sessions include in the given set
141+
*
142+
* @param sessionUUIDs the session UUIDs
143+
* @return a list of session UUIDs of all closed sessions include in the given set*/
144+
Result<List<String>> getAllClosedSessionsIn(Set<String> sessionUUIDs);
138145
}

src/main/java/ch/ethz/seb/sps/server/datalayer/dao/impl/ClientAccessDAOBatis.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,14 @@ public boolean isActive(final String modelId) {
102102
return false;
103103
}
104104

105+
final Long pk = modelIdToPK(modelId);
106+
if (pk == null) {
107+
return false;
108+
}
109+
105110
return this.clientAccessRecordMapper
106111
.countByExample()
107-
.where(ClientAccessRecordDynamicSqlSupport.id, isEqualTo(Long.valueOf(modelId)))
112+
.where(ClientAccessRecordDynamicSqlSupport.id, isEqualTo(pk))
108113
.and(ClientAccessRecordDynamicSqlSupport.terminationTime, SqlBuilder.isNull())
109114
.build()
110115
.execute() > 0;

src/main/java/ch/ethz/seb/sps/server/datalayer/dao/impl/ExamDAOBatis.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,9 +328,14 @@ public boolean isActive(final String modelId) {
328328
return false;
329329
}
330330

331+
final Long pk = modelIdToPK(modelId);
332+
if (pk == null) {
333+
return false;
334+
}
335+
331336
return this.examRecordMapper
332337
.countByExample()
333-
.where(ExamRecordDynamicSqlSupport.id, isEqualTo(Long.valueOf(modelId)))
338+
.where(ExamRecordDynamicSqlSupport.id, isEqualTo(pk))
334339
.and(ExamRecordDynamicSqlSupport.terminationTime, SqlBuilder.isNull())
335340
.build()
336341
.execute() > 0;

src/main/java/ch/ethz/seb/sps/server/datalayer/dao/impl/GroupDAOBatis.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,9 +382,14 @@ public boolean isActive(final String modelId) {
382382
return false;
383383
}
384384

385+
final Long pk = modelIdToPK(modelId);
386+
if (pk == null) {
387+
return false;
388+
}
389+
385390
return this.groupRecordMapper
386391
.countByExample()
387-
.where(GroupRecordDynamicSqlSupport.id, isEqualTo(Long.valueOf(modelId)))
392+
.where(GroupRecordDynamicSqlSupport.id, isEqualTo(pk))
388393
.and(GroupRecordDynamicSqlSupport.terminationTime, SqlBuilder.isNull())
389394
.build()
390395
.execute() > 0;

src/main/java/ch/ethz/seb/sps/server/datalayer/dao/impl/ScreenshotDataDAOBatis.java

Lines changed: 20 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import java.util.Collections;
2020
import java.util.List;
2121
import java.util.Map;
22-
import java.util.Objects;
2322
import java.util.Set;
2423
import java.util.function.Function;
2524
import java.util.stream.Collectors;
@@ -28,7 +27,6 @@
2827
import ch.ethz.seb.sps.server.datalayer.batis.custommappers.ScreenshotDataMapper;
2928
import ch.ethz.seb.sps.server.datalayer.batis.custommappers.SearchApplicationMapper;
3029
import ch.ethz.seb.sps.domain.model.service.UserListForApplicationSearch;
31-
import ch.ethz.seb.sps.utils.Utils;
3230
import org.apache.commons.lang3.StringUtils;
3331
import org.mybatis.dynamic.sql.SqlBuilder;
3432
import org.mybatis.dynamic.sql.select.MyBatis3SelectModelAdapter;
@@ -114,81 +112,42 @@ public Result<Collection<ScreenshotData>> allOf(final Set<Long> pks) {
114112

115113
@Override
116114
@Transactional(readOnly = true)
117-
public Result<Collection<ScreenshotData>> allOfSession(final String sessionUUID) {
115+
public Result<Collection<ScreenshotDataRecord>> allOfSession(final String sessionUUID) {
118116
return Result.tryCatch(() -> this.screenshotDataRecordMapper.selectByExample()
119117
.where(ScreenshotDataRecordDynamicSqlSupport.sessionUuid, SqlBuilder.isEqualTo(sessionUUID))
120118
.build()
121-
.execute()
122-
.stream()
123-
.map(this::toDomainModel)
124-
.collect(Collectors.toList()));
125-
}
126-
127-
@Override
128-
@Transactional(readOnly = true)
129-
public Result<ScreenshotDataRecord> getAt(final String sessionUUID, final Long at) {
130-
return Result.tryCatch(() -> {
131-
return getScreenshotDataAt(sessionUUID, at);
132-
});
133-
}
134-
135-
@Override
136-
@Transactional(readOnly = true)
137-
public Result<Long> getIdAt(final String sessionUUID, final Long at) {
138-
return Result.tryCatch(() -> {
139-
ScreenshotDataRecord screenshotDataAt = getScreenshotDataAt(sessionUUID, at);
140-
141-
if (screenshotDataAt != null) {
142-
return screenshotDataAt.getId();
143-
}
144-
145-
throw new NoResourceFoundException(EntityType.SCREENSHOT_DATA, sessionUUID);
146-
});
147-
}
148-
149-
@Override
150-
@Transactional(readOnly = true)
151-
public Result<Long> getLatestImageId(final String sessionUUID) {
152-
return Result.tryCatch(() -> {
153-
154-
ScreenshotDataRecord screenshotDataAt = getScreenshotDataAt(sessionUUID, null);
155-
if (screenshotDataAt != null) {
156-
return screenshotDataAt.getId();
157-
}
158-
159-
throw new NoResourceFoundException(EntityType.SCREENSHOT_DATA, sessionUUID);
160-
});
119+
.execute());
161120
}
162121

163122
@Override
164123
@Transactional(readOnly = true)
124+
@Deprecated
165125
public Result<ScreenshotDataRecord> getLatest(final String sessionUUID) {
166126
return Result.tryCatch(() -> {
167-
168-
final ScreenshotDataRecord latestScreenshotDataRec = getScreenshotDataAt(sessionUUID, null);
127+
final ScreenshotDataRecord latestScreenshotDataRec = getLatestScreenshotDataRec(sessionUUID);
169128
if (latestScreenshotDataRec == null) {
170129
throw new NoResourceFoundException(EntityType.SCREENSHOT_DATA, sessionUUID);
171130
}
131+
172132
return latestScreenshotDataRec;
173133
});
174134
}
175135

176136
@Override
177-
@Transactional(readOnly = true)
178-
public Result<Map<String, ScreenshotDataRecord>> allLatestIn(final List<String> sessionUUIDs) {
137+
public Result<Map<String, ScreenshotDataRecord>> allOfMappedToSession(final List<Long> pks) {
179138
return Result.tryCatch(() -> {
180-
if (sessionUUIDs == null || sessionUUIDs.isEmpty()) {
139+
140+
if (pks == null || pks.isEmpty()) {
181141
return Collections.emptyMap();
182142
}
183143

184-
// NOTE: For now we use a less efficient version that uses getLatest(final String sessionUUID) for
185-
// all requested sessions but in the future we should solve this problem on DB layer
186-
return sessionUUIDs.stream()
187-
.map(sessionUUID -> getScreenshotDataAt(sessionUUID, null))
188-
.filter(Objects::nonNull)
189-
.collect(Collectors.toMap(
190-
ScreenshotDataRecord::getSessionUuid,
191-
Function.identity()));
144+
return screenshotDataRecordMapper
145+
.selectByExample()
146+
.where(id, isIn(pks))
147+
.build()
148+
.execute()
149+
.stream()
150+
.collect(Collectors.toMap(ScreenshotDataRecord::getSessionUuid, Function.identity()));
192151
});
193152
}
194153

@@ -623,6 +582,11 @@ public Result<List<Long>> getTimestampListForApplicationSearch(
623582
});
624583
}
625584

585+
@Override
586+
public Result<ScreenshotDataRecord> recordByPK(Long pk) {
587+
return Result.tryCatch(() -> this.screenshotDataRecordMapper.selectByPrimaryKey(pk));
588+
}
589+
626590
private ScreenshotDataRecord getLatestScreenshotDataRec(final String sessionUUID) {
627591
return SelectDSL
628592
.selectWithMapper(this.screenshotDataRecordMapper::selectOne,
@@ -660,59 +624,4 @@ private String createMetadataSearchString(String metadataKey, String metadataVal
660624
Constants.PERCENTAGE_STRING;
661625
}
662626

663-
private ScreenshotDataRecord getScreenshotDataAt(final String sessionUUID, final Long at) {
664-
final Long ts = at != null ? at : Utils.getMillisecondsNow();
665-
666-
ScreenshotDataRecord record = getLastScreenshotDataRecordInRange(sessionUUID, ts, ts - 30 * Constants.SECOND_IN_MILLIS);
667-
if (record != null) {
668-
return record;
669-
}
670-
671-
if (log.isDebugEnabled()) {
672-
log.debug("Did not find screenshot within 30s interval, try 1 hour interval");
673-
}
674-
675-
record = getLastScreenshotDataRecordInRange(sessionUUID, ts, ts - Constants.HOUR_IN_MILLIS);
676-
if (record != null) {
677-
return record;
678-
}
679-
680-
if (log.isDebugEnabled()) {
681-
log.debug("Did not find screenshot within 1 hour interval, get first image: {}", sessionUUID);
682-
}
683-
684-
return SelectDSL
685-
.selectWithMapper(this.screenshotDataRecordMapper::selectOne,
686-
id,
687-
sessionUuid,
688-
timestamp,
689-
imageFormat,
690-
metaData,
691-
timestamp)
692-
.from(screenshotDataRecord)
693-
.where(ScreenshotDataRecordDynamicSqlSupport.sessionUuid, SqlBuilder.isEqualTo(sessionUUID))
694-
.limit(1)
695-
.build()
696-
.execute();
697-
}
698-
699-
private ScreenshotDataRecord getLastScreenshotDataRecordInRange(final String sessionUUID, final Long upper, final Long lower) {
700-
return SelectDSL
701-
.selectWithMapper(this.screenshotDataRecordMapper::selectOne,
702-
id,
703-
sessionUuid,
704-
timestamp,
705-
imageFormat,
706-
metaData,
707-
timestamp)
708-
.from(screenshotDataRecord)
709-
.where(ScreenshotDataRecordDynamicSqlSupport.sessionUuid, SqlBuilder.isEqualTo(sessionUUID))
710-
.and(ScreenshotDataRecordDynamicSqlSupport.timestamp, SqlBuilder.isLessThan(upper))
711-
.and(ScreenshotDataRecordDynamicSqlSupport.timestamp, SqlBuilder.isGreaterThanOrEqualToWhenPresent(lower))
712-
.orderBy(timestamp.descending())
713-
.limit(1)
714-
.build()
715-
.execute();
716-
}
717-
718627
}

0 commit comments

Comments
 (0)