diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ProctoringRoomConnection.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ProctoringRoomConnection.java
index f44447de..403443cc 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ProctoringRoomConnection.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ProctoringRoomConnection.java
@@ -25,6 +25,8 @@ public class ProctoringRoomConnection {
public static final String ATTR_ACCESS_TOKEN = "accessToken";
public static final String ATTR_CONNECTION_URL = "connectionURL";
public static final String ATTR_USER_NAME = "userName";
+ public static final String ATTR_ROOM_KEY = "roomKey";
+ public static final String ATTR_API_KEY = "apiKey";
@JsonProperty(ProctoringServiceSettings.ATTR_SERVER_TYPE)
public final ProctoringServerType proctoringServerType;
@@ -45,7 +47,13 @@ public class ProctoringRoomConnection {
public final String subject;
@JsonProperty(ATTR_ACCESS_TOKEN)
- public final String accessToken;
+ public final CharSequence accessToken;
+
+ @JsonProperty(ATTR_ROOM_KEY)
+ public final CharSequence roomKey;
+
+ @JsonProperty(ATTR_API_KEY)
+ public final CharSequence apiKey;
@JsonProperty(ATTR_USER_NAME)
public final String userName;
@@ -58,7 +66,9 @@ public class ProctoringRoomConnection {
@JsonProperty(ATTR_SERVER_URL) final String serverURL,
@JsonProperty(ATTR_ROOM_NAME) final String roomName,
@JsonProperty(ATTR_SUBJECT) final String subject,
- @JsonProperty(ATTR_ACCESS_TOKEN) final String accessToken,
+ @JsonProperty(ATTR_ACCESS_TOKEN) final CharSequence accessToken,
+ @JsonProperty(ATTR_ROOM_KEY) final CharSequence roomKey,
+ @JsonProperty(ATTR_API_KEY) final CharSequence apiKey,
@JsonProperty(ATTR_USER_NAME) final String userName) {
this.proctoringServerType = proctoringServerType;
@@ -68,6 +78,8 @@ public class ProctoringRoomConnection {
this.roomName = roomName;
this.subject = subject;
this.accessToken = accessToken;
+ this.roomKey = roomKey;
+ this.apiKey = apiKey;
this.userName = userName;
}
@@ -83,10 +95,22 @@ public class ProctoringRoomConnection {
return this.serverHost;
}
- public String getAccessToken() {
+ public CharSequence getAccessToken() {
return this.accessToken;
}
+ public CharSequence getRoomKey() {
+ return this.roomKey;
+ }
+
+ public CharSequence getApiKey() {
+ return this.apiKey;
+ }
+
+ public String getUserName() {
+ return this.userName;
+ }
+
public String getServerURL() {
return this.serverURL;
}
diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientInstruction.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientInstruction.java
index eb1bdf24..3c5aa2b1 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientInstruction.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientInstruction.java
@@ -46,16 +46,18 @@ public final class ClientInstruction {
public static final String JITSI_TOKEN = "jitsiMeetToken";
public static final String JITSI_RECEIVE_AUDIO = "jitsiMeetReceiveAudio";
public static final String JITSI_RECEIVE_VIDEO = "jitsiMeetReceiveVideo";
- public static final String JITSI_ALLOW_CHAT = "jitsiMeetFeatureFlagChat";
+ public static final String JITSI_ALLOW_CHAT = "jitsiFeatureFlagChat";
- public static final String ZOOM_URL = "zoomMeetServerURL";
- public static final String ZOOM_ROOM = "zoomMeetRoom";
- public static final String ZOOM_ROOM_SUBJECT = "zoomMeetSubject";
+ public static final String ZOOM_URL = "zoomServerURL";
+ public static final String ZOOM_ROOM = "zoomRoom";
+ public static final String ZOOM_ROOM_SUBJECT = "zoomSubject";
public static final String ZOOM_USER_NAME = "zoomUserName";
- public static final String ZOOM_TOKEN = "zoomMeetToken";
- public static final String ZOOM_RECEIVE_AUDIO = "zoomMeetReceiveAudio";
- public static final String ZOOM_RECEIVE_VIDEO = "zoomMeetReceiveVideo";
- public static final String ZOOM_ALLOW_CHAT = "zoomMeetFeatureFlagChat";
+ public static final String ZOOM_API_KEY = "zoomAPIKey";
+ public static final String ZOOM_TOKEN = "zoomToken";
+ public static final String ZOOM_MEETING_KEY = "zoomMeetingKey";
+ public static final String ZOOM_RECEIVE_AUDIO = "zoomReceiveAudio";
+ public static final String ZOOM_RECEIVE_VIDEO = "zoomReceiveVideo";
+ public static final String ZOOM_ALLOW_CHAT = "zoomFeatureFlagChat";
}
public interface SEB_RECONFIGURE_SETTINGS {
@@ -63,9 +65,9 @@ public final class ClientInstruction {
public static final String JITSI_RECEIVE_VIDEO = "jitsiMeetReceiveVideo";
public static final String JITSI_ALLOW_CHAT = "jitsiMeetFeatureFlagChat";
- public static final String ZOOM_RECEIVE_AUDIO = "zoomMeetReceiveAudio";
- public static final String ZOOM_RECEIVE_VIDEO = "zoomMeetReceiveVideo";
- public static final String ZOOM_ALLOW_CHAT = "zoomMeetFeatureFlagChat";
+ public static final String ZOOM_RECEIVE_AUDIO = "zoomReceiveAudio";
+ public static final String ZOOM_RECEIVE_VIDEO = "zoomReceiveVideo";
+ public static final String ZOOM_ALLOW_CHAT = "zoomFeatureFlagChat";
}
}
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/ProctoringServlet.java b/src/main/java/ch/ethz/seb/sebserver/gui/ProctoringServlet.java
index 5a3b1944..08acad15 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/ProctoringServlet.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/ProctoringServlet.java
@@ -9,6 +9,7 @@
package ch.ethz.seb.sebserver.gui;
import java.io.IOException;
+import java.util.Collection;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
@@ -17,6 +18,8 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;
@@ -25,62 +28,22 @@ import org.springframework.web.context.support.WebApplicationContextUtils;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.SEBServerAuthorizationContext;
-import ch.ethz.seb.sebserver.gui.service.session.ProctoringGUIService;
-import ch.ethz.seb.sebserver.gui.service.session.ProctoringGUIService.ProctoringWindowData;
+import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService;
+import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService.ProctoringWindowData;
+import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringWindowScriptResolver;
@Component
@GuiProfile
public class ProctoringServlet extends HttpServlet {
private static final long serialVersionUID = 3475978419653411800L;
+ private static final Logger log = LoggerFactory.getLogger(ProctoringServlet.class);
- // @formatter:off
- private static final String JITSI_WINDOW_HTML =
- "" +
- "" +
- "
" +
- " " +
- " " +
- "" +
- "" +
- "" +
- " " +
- "" +
- "" +
- "";
- // @formatter:on
+ private final Collection proctoringWindowScriptResolver;
+
+ public ProctoringServlet(final Collection proctoringWindowScriptResolver) {
+ this.proctoringWindowScriptResolver = proctoringWindowScriptResolver;
+ }
@Override
protected void doGet(
@@ -103,21 +66,17 @@ public class ProctoringServlet extends HttpServlet {
(ProctoringWindowData) httpSession
.getAttribute(ProctoringGUIService.SESSION_ATTR_PROCTORING_DATA);
- switch (proctoringData.connectionData.proctoringServerType) {
- case JITSI_MEET: {
- final String script = String.format(
- JITSI_WINDOW_HTML,
- proctoringData.connectionData.serverHost,
- proctoringData.connectionData.roomName,
- proctoringData.connectionData.accessToken,
- proctoringData.connectionData.serverHost,
- proctoringData.connectionData.subject);
- resp.getOutputStream().println(script);
- break;
- }
- default:
- throw new RuntimeException(
- "Unsupported proctoring server type: " + proctoringData.connectionData.proctoringServerType);
+ final String script = this.proctoringWindowScriptResolver.stream()
+ .filter(resolver -> resolver.applies(proctoringData))
+ .findFirst()
+ .map(resolver -> resolver.getProctoringWindowScript(proctoringData))
+ .orElse(null);
+
+ if (script == null) {
+ log.error("Failed to get proctoring window script for data: {}", proctoringData);
+ resp.getOutputStream().println("Failed to get proctoring window script");
+ } else {
+ resp.getOutputStream().println(script);
}
}
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringClientConnection.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringClientConnection.java
index 5a38f437..45b1eabd 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringClientConnection.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringClientConnection.java
@@ -67,7 +67,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetProcto
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
import ch.ethz.seb.sebserver.gui.service.session.ClientConnectionDetails;
import ch.ethz.seb.sebserver.gui.service.session.InstructionProcessor;
-import ch.ethz.seb.sebserver.gui.service.session.ProctoringGUIService;
+import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService;
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition;
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition.TableFilterAttribute;
import ch.ethz.seb.sebserver.gui.table.EntityTable;
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringRunningExam.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringRunningExam.java
index 35e06ee4..1606456b 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringRunningExam.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringRunningExam.java
@@ -81,7 +81,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetTownha
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
import ch.ethz.seb.sebserver.gui.service.session.ClientConnectionTable;
import ch.ethz.seb.sebserver.gui.service.session.InstructionProcessor;
-import ch.ethz.seb.sebserver.gui.service.session.ProctoringGUIService;
+import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService;
import ch.ethz.seb.sebserver.gui.widget.Message;
@Lazy
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ComposerServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ComposerServiceImpl.java
index 7e4af6a3..6e021cb7 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ComposerServiceImpl.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ComposerServiceImpl.java
@@ -33,8 +33,8 @@ import ch.ethz.seb.sebserver.gui.service.page.RemoteProctoringView;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.IllegalUserSessionStateException;
-import ch.ethz.seb.sebserver.gui.service.session.ProctoringGUIService;
-import ch.ethz.seb.sebserver.gui.service.session.ProctoringGUIService.ProctoringWindowData;
+import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService;
+import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService.ProctoringWindowData;
import ch.ethz.seb.sebserver.gui.widget.Message;
@Lazy
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/JitsiMeetProctoringView.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/JitsiMeetProctoringView.java
index 9707eb5e..86b37772 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/JitsiMeetProctoringView.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/JitsiMeetProctoringView.java
@@ -33,8 +33,8 @@ import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
import ch.ethz.seb.sebserver.gui.service.page.RemoteProctoringView;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.SendProctoringReconfigurationAttributes;
-import ch.ethz.seb.sebserver.gui.service.session.ProctoringGUIService;
-import ch.ethz.seb.sebserver.gui.service.session.ProctoringGUIService.ProctoringWindowData;
+import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService;
+import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService.ProctoringWindowData;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@Component
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/push/ServerPushService.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/push/ServerPushService.java
index 8fb7269b..291a8acb 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/service/push/ServerPushService.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/push/ServerPushService.java
@@ -90,8 +90,9 @@ public class ServerPushService {
log.warn(
"Failed to stop Server Push Session on: {}. "
+ "It seems that the UISession is not available anymore. "
- + "This may source from a connection interruption",
- Thread.currentThread().getName(), e);
+ + "This may source from a connection interruption. Cause: {}",
+ Thread.currentThread().getName(),
+ e.getMessage());
}
});
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/CurrentUser.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/CurrentUser.java
index d05cdc48..cb0d8dc3 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/CurrentUser.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/CurrentUser.java
@@ -35,7 +35,7 @@ import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
-import ch.ethz.seb.sebserver.gui.service.session.ProctoringGUIService;
+import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService;
@Component
@GuiProfile
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/JitsiWindowScriptResolver.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/JitsiWindowScriptResolver.java
new file mode 100644
index 00000000..a1baea70
--- /dev/null
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/JitsiWindowScriptResolver.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2021 ETH Zürich, Educational Development and Technology (LET)
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+package ch.ethz.seb.sebserver.gui.service.session.proctoring;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.text.StringSubstitutor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+
+import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType;
+import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
+import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService.ProctoringWindowData;
+
+@Lazy
+@Service
+@GuiProfile
+public class JitsiWindowScriptResolver implements ProctoringWindowScriptResolver {
+
+ private static final Logger log = LoggerFactory.getLogger(JitsiWindowScriptResolver.class);
+
+ private static final String ATTR_SUBJECT = "_subject_";
+ private static final String ATTR_ACCESS_TOKEN = "_accessToken_";
+ private static final String ATTR_ROOM_NAME = "_roomName_";
+ private static final String ATTR_HOST = "_host_";
+
+ // @formatter:off
+ private static final String JITSI_WINDOW_HTML =
+ "" +
+ "" +
+ "" +
+ " " +
+ " " +
+ "" +
+ "" +
+ "" +
+ " " +
+ "" +
+ "" +
+ "";
+ // @formatter:on
+
+ @Override
+ public boolean applies(final ProctoringWindowData data) {
+ try {
+ return data.connectionData.proctoringServerType == ProctoringServerType.JITSI_MEET;
+ } catch (final Exception e) {
+ log.error("Failed to verify responsibility. Cause: {}", e.getMessage());
+ return false;
+ }
+ }
+
+ @Override
+ public String getProctoringWindowScript(final ProctoringWindowData data) {
+ final Map args = new HashMap<>();
+ args.put(ATTR_HOST, data.connectionData.serverHost);
+ args.put(ATTR_ROOM_NAME, data.connectionData.roomName);
+ args.put(ATTR_ACCESS_TOKEN, String.valueOf(data.connectionData.accessToken));
+ args.put(ATTR_SUBJECT, data.connectionData.subject);
+
+ return new StringSubstitutor(args, "%%_", "_%%")
+ .replace(JITSI_WINDOW_HTML);
+ }
+
+}
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ProctoringGUIService.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/ProctoringGUIService.java
similarity index 96%
rename from src/main/java/ch/ethz/seb/sebserver/gui/service/session/ProctoringGUIService.java
rename to src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/ProctoringGUIService.java
index 269f6424..30ecce8c 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ProctoringGUIService.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/ProctoringGUIService.java
@@ -6,7 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-package ch.ethz.seb.sebserver.gui.service.session;
+package ch.ethz.seb.sebserver.gui.service.session.proctoring;
import java.util.Collection;
import java.util.HashMap;
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/ProctoringWindowScriptResolver.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/ProctoringWindowScriptResolver.java
new file mode 100644
index 00000000..84332244
--- /dev/null
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/ProctoringWindowScriptResolver.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2021 ETH Zürich, Educational Development and Technology (LET)
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+package ch.ethz.seb.sebserver.gui.service.session.proctoring;
+
+import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService.ProctoringWindowData;
+
+public interface ProctoringWindowScriptResolver {
+
+ boolean applies(ProctoringWindowData data);
+
+ String getProctoringWindowScript(ProctoringWindowData data);
+
+}
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/ZoomWindowScriptResolver.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/ZoomWindowScriptResolver.java
new file mode 100644
index 00000000..57cfa9b5
--- /dev/null
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/ZoomWindowScriptResolver.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2021 ETH Zürich, Educational Development and Technology (LET)
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+package ch.ethz.seb.sebserver.gui.service.session.proctoring;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.text.StringSubstitutor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+
+import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType;
+import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
+import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService.ProctoringWindowData;
+
+@Lazy
+@Service
+@GuiProfile
+public class ZoomWindowScriptResolver implements ProctoringWindowScriptResolver {
+
+ private static final Logger log = LoggerFactory.getLogger(ZoomWindowScriptResolver.class);
+
+ private static final String ATTR_SUBJECT = "_subject_";
+ private static final String ATTR_API_KEY = "_apiKey_";
+ private static final String ATTR_ACCESS_TOKEN = "_accessToken_";
+ private static final String ATTR_ROOM_KEY = "_roomKey_";
+ private static final String ATTR_ROOM_NAME = "_roomName_";
+ private static final String ATTR_HOST = "_host_";
+ private static final String ATTR_USER_NAME = "_username_";
+
+ // @formatter:off
+ private static final String ZOOM_WINDOW_HTML =
+ "\n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + "";
+ // @formatter:on
+
+ @Override
+ public boolean applies(final ProctoringWindowData data) {
+ try {
+ return data.connectionData.proctoringServerType == ProctoringServerType.ZOOM;
+ } catch (final Exception e) {
+ log.error("Failed to verify responsibility. Cause: {}", e.getMessage());
+ return false;
+ }
+ }
+
+ @Override
+ public String getProctoringWindowScript(final ProctoringWindowData data) {
+ final Map args = new HashMap<>();
+ args.put(ATTR_HOST, data.connectionData.serverHost);
+ args.put(ATTR_ROOM_NAME, data.connectionData.roomName);
+ args.put(ATTR_ACCESS_TOKEN, String.valueOf(data.connectionData.accessToken));
+ args.put(ATTR_API_KEY, String.valueOf(data.connectionData.apiKey));
+ if (StringUtils.isNotBlank(data.connectionData.roomKey)) {
+ args.put(ATTR_ROOM_KEY, String.valueOf(data.connectionData.roomKey));
+ } else {
+ args.put(ATTR_ROOM_KEY, "");
+ }
+ args.put(ATTR_SUBJECT, data.connectionData.subject);
+ args.put(ATTR_USER_NAME, data.connectionData.userName);
+
+ return new StringSubstitutor(args, "%%_", "_%%")
+ .replace(ZOOM_WINDOW_HTML);
+ }
+
+}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/RemoteProctoringRoomDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/RemoteProctoringRoomDAO.java
index 82e3f63e..d1d936f0 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/RemoteProctoringRoomDAO.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/RemoteProctoringRoomDAO.java
@@ -16,35 +16,99 @@ import ch.ethz.seb.sebserver.gbl.model.session.RemoteProctoringRoom;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.proctoring.NewRoom;
+/** Data access for RemoteProctoringRoom domain objects. */
public interface RemoteProctoringRoomDAO {
- Result> getCollectingRoomsForExam(Long examId);
+ /** Get all collecting room records that exists for a given exam.
+ *
+ * @param examId the exam identifier
+ * @return Result refer to the collection of all collecting room records for the given exam or to an error when
+ * happened */
+ Result> getCollectingRooms(Long examId);
- //Result> getRoomsOfExam(Long examId);
+ /** Get all room records that exists for a given exam.
+ *
+ * @param examId the exam identifier
+ * @return Result refer to the collection of all room records for the given exam or to an error when
+ * happened */
+ Result> getRooms(Long examId);
+ /** The the room record with given identifier (PK).
+ *
+ * @param roomId the room record identifier
+ * @return Result refer to the room record or to an error when happened */
Result getRoom(Long roomId);
+ /** Get the room record with specified name for a given exam.
+ *
+ * @param examId the exam identifier
+ * @param roomName the name of the room
+ * @return Result refer to the room record or to an error when happened */
Result getRoom(Long examId, String roomName);
+ /** Get the room name for the room with given identifier.
+ *
+ * @param roomId the room record identifier (PK)
+ * @return Result refer to the rooms name or to an error when happened */
Result getRoomName(Long roomId);
+ /** Create the town hall room for a given exam. Uses the given room data to create the record
+ *
+ * @param examId the exam identifier
+ * @param room the room data to save to record
+ * @return Result refer to the created room record or to an error when happened */
Result createTownhallRoom(Long examId, NewRoom room);
+ /** Get the town hall room record for a given exam if existing.
+ *
+ * @param examId the exam identifier
+ * @return Result refer to the town-hall room record or to an error when happened. */
Result getTownhallRoom(Long examId);
+ /** Delete the town-hall room record for a given exam.
+ *
+ * @param examId the exam identifier
+ * @return Result refer to the entity key of the former town-hall room record or to an error when happened */
Result deleteTownhallRoom(Long examId);
+ /** Create a break-out room for a given exam. Uses the given room data to create the record
+ *
+ * @param examId the exam identifier
+ * @param room the room data to save to record
+ * @param connectionTokens comma separated list of SEB client connection tokens that joins the new break-out room
+ * @return Result refer to the created break-out room record or to an error when happened */
Result createBreakOutRoom(Long examId, NewRoom room, String connectionTokens);
+ /** Delete the room record with given id.
+ *
+ * @param roomId the room identifier (PK)
+ * @return Result refer to the entity key of the former room record or to an error when happened */
Result deleteRoom(Long roomId);
+ /** Delete all room records for a given exam.
+ *
+ * @param examId the exam identifier
+ * @return Result refer to a collection of entity keys for all delete room records or to an error when happened */
Result> deleteRooms(Long examId);
+ /** This reserves a place in a collecting room on a given exam.
+ * Creates a new collecting room record depending on the roomMaxSize and the how many connection
+ * already have been collected within the actual collecting room.
+ *
+ * @param examId the exam identifier
+ * @param roomMaxSize the maximum size of connection collected in one collecting room
+ * @param newRoomFunction Function to create data for a new collecting room if needed.
+ * @return Result refer to the collecting room record of place or to an error when happened */
Result reservePlaceInCollectingRoom(
Long examId,
int roomMaxSize,
Function> newRoomFunction);
+ /** Releases a place in the actual collecting room for a given exam.
+ *
+ * @param examId the exam identifier
+ * @param roomId the room record identifier (PK)
+ * @return Result refer to the actual collecting room record or to an error when happened */
Result releasePlaceInCollectingRoom(final Long examId, Long roomId);
}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/RemoteProctoringRoomDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/RemoteProctoringRoomDAOImpl.java
index 68f64112..41840c6a 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/RemoteProctoringRoomDAOImpl.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/RemoteProctoringRoomDAOImpl.java
@@ -54,7 +54,7 @@ public class RemoteProctoringRoomDAOImpl implements RemoteProctoringRoomDAO {
@Override
@Transactional(readOnly = true)
- public Result> getCollectingRoomsForExam(final Long examId) {
+ public Result> getCollectingRooms(final Long examId) {
return Result.tryCatch(() -> this.remoteProctoringRoomRecordMapper.selectByExample()
.where(RemoteProctoringRoomRecordDynamicSqlSupport.examId, isEqualTo(examId))
.and(RemoteProctoringRoomRecordDynamicSqlSupport.townhallRoom, isEqualTo(0))
@@ -66,6 +66,18 @@ public class RemoteProctoringRoomDAOImpl implements RemoteProctoringRoomDAO {
.collect(Collectors.toList()));
}
+ @Override
+ @Transactional(readOnly = true)
+ public Result> getRooms(final Long examId) {
+ return Result.tryCatch(() -> this.remoteProctoringRoomRecordMapper.selectByExample()
+ .where(RemoteProctoringRoomRecordDynamicSqlSupport.examId, isEqualTo(examId))
+ .build()
+ .execute()
+ .stream()
+ .map(this::toDomainModel)
+ .collect(Collectors.toList()));
+ }
+
@Override
@Transactional(readOnly = true)
public Result getRoom(final Long roomId) {
@@ -75,6 +87,7 @@ public class RemoteProctoringRoomDAOImpl implements RemoteProctoringRoomDAO {
}
@Override
+ @Transactional(readOnly = true)
public Result getRoom(final Long examId, final String roomName) {
return Result.tryCatch(() -> {
return this.remoteProctoringRoomRecordMapper.selectByExample()
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPIServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPIServiceImpl.java
index a4a2672f..1553f9fa 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPIServiceImpl.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPIServiceImpl.java
@@ -81,7 +81,9 @@ public class LmsAPIServiceImpl implements LmsAPIService {
return;
}
- log.debug("LmsSetup changed. Update cache by removing eventually used references");
+ if (log.isDebugEnabled()) {
+ log.debug("LmsSetup changed. Update cache by removing eventually used references");
+ }
this.cache.remove(new CacheKey(lmsSetup.getModelId(), 0));
}
@@ -105,11 +107,6 @@ public class LmsAPIServiceImpl implements LmsAPIService {
@Override
public Result getLmsAPITemplate(final String lmsSetupId) {
-
- if (log.isDebugEnabled()) {
- log.debug("Get LmsAPITemplate for id: {}", lmsSetupId);
- }
-
return Result.tryCatch(() -> this.lmsSetupDAO
.byModelId(lmsSetupId)
.getOrThrow())
@@ -214,7 +211,6 @@ public class LmsAPIServiceImpl implements LmsAPIService {
.forEach(this.cache::remove);
// get from cache
return this.cache.get(new CacheKey(lmsSetup.getModelId(), 0));
-
}
private LmsAPITemplate createLmsSetupTemplate(final LmsSetup lmsSetup) {
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamProctoringService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamProctoringService.java
index b2192a1e..a96b86b9 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamProctoringService.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamProctoringService.java
@@ -30,7 +30,7 @@ public interface ExamProctoringService {
*
* @param proctoringSettings the settings to test
* @return Result refer to true if the settings are correct and the proctoring server can be accessed. */
- Result testExamProctoring(final ProctoringServiceSettings proctoringSettings);
+ Result testExamProctoring(ProctoringServiceSettings proctoringSettings);
/** Gets the room connection data for a certain room for the proctor.
*
@@ -45,21 +45,14 @@ public interface ExamProctoringService {
String subject);
Result getClientRoomConnection(
- final ProctoringServiceSettings proctoringSettings,
- final String connectionToken,
- final String roomName,
- final String subject);
+ ProctoringServiceSettings proctoringSettings,
+ String connectionToken,
+ String roomName,
+ String subject);
-// Result getClientCollectingRoomConnection(
-// final ProctoringServiceSettings proctoringSettings,
-// final String connectionToken,
-// final String roomName,
-// final String subject);
+ Map createJoinInstructionAttributes(ProctoringRoomConnection proctoringConnection);
- Map createJoinInstructionAttributes(
- final ProctoringRoomConnection proctoringConnection);
-
- Result disposeServiceRoomsForExam(Exam exam);
+ Result disposeServiceRoomsForExam(ProctoringServiceSettings proctoringSettings, Exam exam);
default String verifyRoomName(final String requestedRoomName, final String connectionToken) {
if (StringUtils.isNotBlank(requestedRoomName)) {
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamProctoringRoomServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamProctoringRoomServiceImpl.java
index cb7a0f66..ce395bec 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamProctoringRoomServiceImpl.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamProctoringRoomServiceImpl.java
@@ -70,7 +70,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
@Override
public Result> getProctoringCollectingRooms(final Long examId) {
- return this.remoteProctoringRoomDAO.getCollectingRoomsForExam(examId);
+ return this.remoteProctoringRoomDAO.getCollectingRooms(examId);
}
@Override
@@ -115,9 +115,14 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
return Result.tryCatch(() -> {
+ final ProctoringServiceSettings settings = this.examSessionService
+ .getRunningExam(exam.id)
+ .flatMap(this.examAdminService::getProctoringServiceSettings)
+ .getOrThrow();
+
this.examAdminService
.getExamProctoringService(exam)
- .flatMap(service -> service.disposeServiceRoomsForExam(exam))
+ .flatMap(service -> service.disposeServiceRoomsForExam(settings, exam))
.onError(error -> log.error("Failed to dispose proctoring service rooms for exam: {} / {}",
exam.name,
exam.externalId,
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/JitsiProctoringService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/JitsiProctoringService.java
index de6e9e65..9b4bae6f 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/JitsiProctoringService.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/JitsiProctoringService.java
@@ -164,7 +164,10 @@ public class JitsiProctoringService implements ExamProctoringService {
}
@Override
- public Result disposeServiceRoomsForExam(final Exam exam) {
+ public Result disposeServiceRoomsForExam(
+ final ProctoringServiceSettings proctoringSettings,
+ final Exam exam) {
+
// NOTE: Since Jitsi rooms are generated and disposed automatically we don't need to do anything here
return Result.EMPTY;
}
@@ -234,7 +237,7 @@ public class JitsiProctoringService implements ExamProctoringService {
}
attributes.put(
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.JITSI_TOKEN,
- proctoringConnection.accessToken);
+ String.valueOf(proctoringConnection.accessToken));
return attributes;
}
@@ -324,6 +327,8 @@ public class JitsiProctoringService implements ExamProctoringService {
roomName,
subject,
token,
+ null,
+ null,
clientName);
});
}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomProctoringService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomProctoringService.java
index 32aa5981..2e2f408c 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomProctoringService.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomProctoringService.java
@@ -204,17 +204,27 @@ public class ZoomProctoringService implements ExamProctoringService {
attributes.put(
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.ZOOM_ROOM,
proctoringConnection.roomName);
+ attributes.put(
+ ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.ZOOM_TOKEN,
+ String.valueOf(proctoringConnection.accessToken));
+ if (StringUtils.isNotBlank(proctoringConnection.apiKey)) {
+ attributes.put(
+ ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.ZOOM_API_KEY,
+ String.valueOf(proctoringConnection.apiKey));
+ }
+ if (StringUtils.isNotBlank(proctoringConnection.roomKey)) {
+ attributes.put(
+ ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.ZOOM_MEETING_KEY,
+ String.valueOf(proctoringConnection.roomKey));
+ }
+ attributes.put(
+ ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.ZOOM_USER_NAME,
+ proctoringConnection.userName);
if (StringUtils.isNotBlank(proctoringConnection.subject)) {
attributes.put(
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.ZOOM_ROOM_SUBJECT,
proctoringConnection.subject);
}
- attributes.put(
- ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.ZOOM_TOKEN,
- proctoringConnection.accessToken);
- attributes.put(
- ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.ZOOM_USER_NAME,
- proctoringConnection.userName);
return attributes;
}
@@ -250,6 +260,8 @@ public class ZoomProctoringService implements ExamProctoringService {
roomName,
subject,
jwt,
+ credentials.accessToken,
+ credentials.clientId,
this.authorizationService.getUserService().getCurrentUser().getUsername());
});
}
@@ -290,18 +302,29 @@ public class ZoomProctoringService implements ExamProctoringService {
roomName,
subject,
jwt,
+ credentials.accessToken,
+ credentials.clientId,
clientConnection.clientConnection.userSessionId);
});
}
@Override
- public Result disposeServiceRoomsForExam(final Exam exam) {
+ public Result disposeServiceRoomsForExam(
+ final ProctoringServiceSettings proctoringSettings,
+ final Exam exam) {
return Result.tryCatch(() -> {
- //this.remoteProctoringRoomDAO.getRoomsOfExam(exam.id);
+ this.remoteProctoringRoomDAO
+ .getRooms(exam.id)
+ .getOrThrow()
+ .stream()
+ .forEach(room -> {
+ disposeBreakOutRoom(proctoringSettings, room.name)
+ .onError(error -> log.warn("Failed to dispose proctoring room record for: {} cause: {}",
+ room,
+ error.getMessage()));
+ });
});
- // Get all rooms of the exam
-
}
@Override
@@ -492,7 +515,7 @@ public class ZoomProctoringService implements ExamProctoringService {
final String jwtHeaderPart = urlEncoder.encodeToString(
ZOOM_ACCESS_TOKEN_HEADER.getBytes(StandardCharsets.UTF_8));
final String jwtPayload = String.format(
- ZOOM_API_ACCESS_TOKEN_PAYLOAD.replaceAll(" ", "").replaceAll("\n", ""),
+ ZOOM_MEETING_ACCESS_TOKEN_PAYLOAD.replaceAll(" ", "").replaceAll("\n", ""),
credentials.clientIdAsString(),
iat,
exp,