SEBSERV-138 ARIA plugin integration

+ Database integrity checks
This commit is contained in:
anhefti 2021-06-16 13:27:58 +02:00
parent 9a788cc495
commit bcb21da221
10 changed files with 349 additions and 1 deletions

12
pom.xml
View file

@ -322,6 +322,18 @@
<artifactId>org.eclipse.rap.fileupload</artifactId>
<version>3.7.0</version>
</dependency>
<dependency>
<groupId>org.xeustechnologies</groupId>
<artifactId>jcl-core</artifactId>
<version>2.8</version>
</dependency>
<!-- <dependency> -->
<!-- <groupId>com.eclipsesource.rap.aria</groupId> -->
<!-- <artifactId>aria</artifactId> -->
<!-- <version>1.0</version> -->
<!-- <scope>system</scope> -->
<!-- <systemPath>C:\dev\workspaces\sebserver\externalResources\lib\com.eclipsesource.rap.aria_0.4.0.20210614-0915.jar</systemPath> -->
<!-- </dependency> -->
<!-- Misc -->
<dependency>

View file

@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.gui;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
@ -17,6 +18,7 @@ import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.application.AbstractEntryPoint;
import org.eclipse.rap.rwt.application.Application;
@ -33,6 +35,9 @@ import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.xeustechnologies.jcl.JarClassLoader;
//import com.eclipsesource.rap.aria.Aria;
import ch.ethz.seb.sebserver.gui.service.remote.download.DownloadService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
@ -72,6 +77,7 @@ public class RAPConfiguration implements ApplicationConfiguration {
application.addEntryPoint(guiEntrypoint, new RAPSpringEntryPointFactory(), properties);
application.addEntryPoint(proctoringEntrypoint, new RAPRemoteProcotringEntryPointFactory(), properties);
} catch (final RuntimeException re) {
throw re;
} catch (final Exception e) {
@ -99,6 +105,7 @@ public class RAPConfiguration implements ApplicationConfiguration {
@Override
protected void createContents(final Composite parent) {
final HttpSession httpSession = RWT
.getUISession(parent.getDisplay())
.getHttpSession();
@ -120,6 +127,7 @@ public class RAPConfiguration implements ApplicationConfiguration {
public static final class RAPSpringEntryPointFactory implements EntryPointFactory {
private final JarClassLoader jcl = new JarClassLoader();
private boolean initialized = false;
@Override
@ -154,6 +162,28 @@ public class RAPConfiguration implements ApplicationConfiguration {
final WebApplicationContext webApplicationContext = getWebApplicationContext(httpSession);
initSpringBasedRAPServices(webApplicationContext);
final String ariaPluginPath = ariaPluginPath(webApplicationContext);
if (StringUtils.isNotBlank(ariaPluginPath)) {
log.debug("Try to initialize com.eclipsesource.rap.aria.Aria plugin...");
try {
final Class<?> forName = Class.forName(
"com.eclipsesource.rap.aria.Aria",
false,
RAPSpringEntryPointFactory.this.jcl);
final Method method = forName.getMethod("activate");
method.invoke(null);
log.info("Initialization of com.eclipsesource.rap.aria.Aria plugin was successful");
} catch (final Exception e) {
log.error("Failed to initialize com.eclipsesource.rap.aria.Aria plugin: ", e);
}
}
final EntryPointService entryPointService = webApplicationContext
.getBean(EntryPointService.class);
@ -172,12 +202,25 @@ public class RAPConfiguration implements ApplicationConfiguration {
final ServiceManager manager = RWT.getServiceManager();
final DownloadService downloadService = webApplicationContext.getBean(DownloadService.class);
manager.registerServiceHandler(DownloadService.DOWNLOAD_SERVICE_NAME, downloadService);
final String ariaPluginPath = ariaPluginPath(webApplicationContext);
if (StringUtils.isNotBlank(ariaPluginPath)) {
this.jcl.add(ariaPluginPath);
}
this.initialized = true;
} catch (final IllegalArgumentException iae) {
log.warn("Failed to register DownloadService on ServiceManager. Already registered: ", iae);
}
}
}
private String ariaPluginPath(final WebApplicationContext webApplicationContext) {
return webApplicationContext
.getEnvironment()
.getProperty("sebserver.gui.external.lib.aria.plugin.path", "");
}
}
private static boolean isAuthenticated(

View file

@ -0,0 +1,21 @@
/*
* 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.webservice;
import ch.ethz.seb.sebserver.gbl.util.Result;
public interface DBIntegrityCheck {
String name();
String description();
Result<String> applyCheck(boolean tryFix);
}

View file

@ -0,0 +1,73 @@
/*
* 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.webservice;
import java.util.Collection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.SEBServerInit;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
@Lazy
@Component
@WebServiceProfile
public class DBIntegrityChecker {
private static final Logger log = LoggerFactory.getLogger(DBIntegrityChecker.class);
private final Collection<DBIntegrityCheck> checkers;
private final boolean runIntegrityChecks;
private final boolean tryFix;
public DBIntegrityChecker(
final Collection<DBIntegrityCheck> checkers,
@Value("${sebserver.init.database.integrity.checks:true}") final boolean runIntegrityChecks,
@Value("${sebserver.init.database.integrity.try-fix:true}") final boolean tryFix) {
this.checkers = checkers;
this.runIntegrityChecks = runIntegrityChecks;
this.tryFix = tryFix;
}
public void checkIntegrity() {
if (this.runIntegrityChecks && !this.checkers.isEmpty()) {
SEBServerInit.INIT_LOGGER.info("---->");
SEBServerInit.INIT_LOGGER.info("----> **** Run data-base integrity checks ****");
SEBServerInit.INIT_LOGGER.info("---->");
this.checkers.stream().forEach(this::doCheck);
}
}
private void doCheck(final DBIntegrityCheck dbIntegrityCheck) {
try {
SEBServerInit.INIT_LOGGER.info("------> Apply check: {} / {}", dbIntegrityCheck.name(),
dbIntegrityCheck.description());
final Result<String> applyCheck = dbIntegrityCheck.applyCheck(this.tryFix);
if (applyCheck.hasError()) {
SEBServerInit.INIT_LOGGER.info("--------> Unexpected Error: {}", applyCheck.getError().getMessage());
} else {
SEBServerInit.INIT_LOGGER.info("--------> Result: {}", applyCheck.get());
}
} catch (final Exception e) {
log.error("Unexpected error while trying to apply data base integrity check: {}", dbIntegrityCheck);
}
}
}

View file

@ -37,6 +37,7 @@ public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent
private final AdminUserInitializer adminUserInitializer;
private final ApplicationEventPublisher applicationEventPublisher;
private final WebserviceInfoDAO webserviceInfoDAO;
private final DBIntegrityChecker dbIntegrityChecker;
protected WebserviceInit(
final SEBServerInit sebServerInit,
@ -44,7 +45,8 @@ public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent
final WebserviceInfo webserviceInfo,
final AdminUserInitializer adminUserInitializer,
final ApplicationEventPublisher applicationEventPublisher,
final WebserviceInfoDAO webserviceInfoDAO) {
final WebserviceInfoDAO webserviceInfoDAO,
final DBIntegrityChecker dbIntegrityChecker) {
this.sebServerInit = sebServerInit;
this.environment = environment;
@ -52,6 +54,7 @@ public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent
this.adminUserInitializer = adminUserInitializer;
this.applicationEventPublisher = applicationEventPublisher;
this.webserviceInfoDAO = webserviceInfoDAO;
this.dbIntegrityChecker = dbIntegrityChecker;
}
@Override
@ -108,6 +111,9 @@ public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent
SEBServerInit.INIT_LOGGER.info("---->");
SEBServerInit.INIT_LOGGER.info("----> Property Override Test: {}", this.webserviceInfo.getTestProperty());
// Run the data base integrity checks and fixes if configured
this.dbIntegrityChecker.checkIntegrity();
// Create an initial admin account if requested and not already in the data-base
this.adminUserInitializer.initAdminAccount();

View file

@ -0,0 +1,93 @@
/*
* 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.webservice.datalayer.checks;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.DBIntegrityCheck;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.OrientationRecordMapper;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.OrientationRecord;
@Lazy
@Component
@WebServiceProfile
public class OrientationTableDuplicatesCheck implements DBIntegrityCheck {
private final OrientationRecordMapper orientationRecordMapper;
public OrientationTableDuplicatesCheck(final OrientationRecordMapper orientationRecordMapper) {
this.orientationRecordMapper = orientationRecordMapper;
}
@Override
public String name() {
return "OrientationTableDuplicatesCheck";
}
@Override
public String description() {
return "Checks if there are duplicate entries in the orientation table by using the config_attribute_id and template_id to identify duplicates.";
}
@Override
@Transactional
public Result<String> applyCheck(final boolean tryFix) {
return Result.tryCatch(() -> {
final List<OrientationRecord> records = this.orientationRecordMapper
.selectByExample()
.build()
.execute();
final Set<String> once = new HashSet<>();
final Set<Long> toDelete = new HashSet<>();
for (final OrientationRecord record : records) {
final String id = record.getConfigAttributeId().toString() + record.getTemplateId().toString();
if (once.contains(id)) {
toDelete.add(record.getId());
} else {
once.add(id);
}
}
if (toDelete.isEmpty()) {
return "OK";
}
if (tryFix) {
toDelete
.stream()
.forEach(this.orientationRecordMapper::deleteByPrimaryKey);
return "Fixed duplicates by deletion: " + toDelete;
} else {
return "Found duplicates: " + toDelete;
}
});
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("OrientationTableDuplicatesCheck [name()=");
builder.append(name());
builder.append(", description()=");
builder.append(description());
builder.append("]");
return builder.toString();
}
}

View file

@ -0,0 +1,93 @@
/*
* 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.webservice.datalayer.checks;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.DBIntegrityCheck;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ViewRecordMapper;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ViewRecord;
@Lazy
@Component
@WebServiceProfile
public class ViewTableDuplicatesCheck implements DBIntegrityCheck {
private final ViewRecordMapper viewRecordMapper;
public ViewTableDuplicatesCheck(final ViewRecordMapper viewRecordMapper) {
this.viewRecordMapper = viewRecordMapper;
}
@Override
public String name() {
return "ViewTableDuplicatesCheck";
}
@Override
public String description() {
return "Checks if there are duplicate entries in the view table by using the name and template_id to identify duplicates.";
}
@Override
@Transactional
public Result<String> applyCheck(final boolean tryFix) {
return Result.tryCatch(() -> {
final List<ViewRecord> records = this.viewRecordMapper
.selectByExample()
.build()
.execute();
final Set<String> once = new HashSet<>();
final Set<Long> toDelete = new HashSet<>();
for (final ViewRecord record : records) {
final String id = record.getName() + record.getTemplateId().toString();
if (once.contains(id)) {
toDelete.add(record.getId());
} else {
once.add(id);
}
}
if (toDelete.isEmpty()) {
return "OK";
}
if (tryFix) {
toDelete
.stream()
.forEach(this.viewRecordMapper::deleteByPrimaryKey);
return "Fixed duplicates by deletion: " + toDelete;
} else {
return "Found duplicates: " + toDelete;
}
});
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("ViewTableDuplicatesCheck [name()=");
builder.append(name());
builder.append(", description()=");
builder.append(description());
builder.append("]");
return builder.toString();
}
}

View file

@ -1,5 +1,7 @@
spring.profiles.include=dev-ws,dev-gui
sebserver.test.property=This is the development Setup
server.address=localhost
server.port=8080
server.servlet.context-path=/

View file

@ -38,6 +38,9 @@ sebserver.gui.filter.date.from.years=2
sebserver.gui.remote.proctoring.entrypoint=/remote-proctoring
sebserver.gui.remote.proctoring.api-servler.endpoint=/remote-view-servlet
# external libs / plugins
sebserver.gui.external.lib.aria.plugin.path=
# Webservice connection details
sebserver.webservice.api.exam.endpoint=/exam-api
sebserver.webservice.api.exam.endpoint.discovery=${sebserver.webservice.api.exam.endpoint}/discovery

View file

@ -7,6 +7,8 @@ sebserver.test.property=This is the default/root configuration
sebserver.init.adminaccount.gen-on-init=true
sebserver.init.organisation.name=SEB Server
sebserver.init.adminaccount.username=sebserver-admin
sebserver.init.database.integrity.checks=true
sebserver.init.database.integrity.try-fix=true
### webservice caching
spring.cache.jcache.provider=org.ehcache.jsr107.EhcacheCachingProvider