SEBSERV-250 improved webservice init
This commit is contained in:
parent
bab4b95609
commit
dfb8afb740
18 changed files with 235 additions and 144 deletions
|
@ -10,11 +10,16 @@ package ch.ethz.seb.sebserver;
|
|||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
import ch.ethz.seb.sebserver.webservice.WebserviceInit;
|
||||
|
||||
public class SEBServerInitEvent extends ApplicationEvent {
|
||||
|
||||
private static final long serialVersionUID = -3608628289559324471L;
|
||||
|
||||
public SEBServerInitEvent(final Object source) {
|
||||
super(source);
|
||||
public final WebserviceInit webserviceInit;
|
||||
|
||||
public SEBServerInitEvent(final WebserviceInit webserviceInit) {
|
||||
super(webserviceInit);
|
||||
this.webserviceInit = webserviceInit;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,16 +43,10 @@ public class GuiInit implements ApplicationListener<ApplicationReadyEvent> {
|
|||
|
||||
this.sebServerInit.init();
|
||||
|
||||
SEBServerInit.INIT_LOGGER.info("---->");
|
||||
SEBServerInit.INIT_LOGGER.info("----> **** GUI Service starting up... ****");
|
||||
|
||||
SEBServerInit.INIT_LOGGER.info("---->");
|
||||
SEBServerInit.INIT_LOGGER.info("----> GUI Service successfully successfully started up!");
|
||||
SEBServerInit.INIT_LOGGER.info("---->");
|
||||
|
||||
// final String webServiceProtocol = this.environment.getProperty("sebserver.gui.webservice.protocol", "http");
|
||||
// final String webServiceAddress = this.environment.getRequiredProperty("sebserver.gui.webservice.address");
|
||||
// final String webServicePort = this.environment.getProperty("sebserver.gui.webservice.port", "80");
|
||||
SEBServerInit.INIT_LOGGER.info("----> *********************************************************");
|
||||
SEBServerInit.INIT_LOGGER.info("----> *** GUI Service starting up... ***");
|
||||
SEBServerInit.INIT_LOGGER.info("----> *********************************************************");
|
||||
SEBServerInit.INIT_LOGGER.info("----> ");
|
||||
|
||||
SEBServerInit.INIT_LOGGER.info(
|
||||
"----> Webservice connection: {}",
|
||||
|
@ -82,6 +76,11 @@ public class GuiInit implements ApplicationListener<ApplicationReadyEvent> {
|
|||
SEBServerInit.INIT_LOGGER.info("---->");
|
||||
SEBServerInit.INIT_LOGGER.info("----> Webservice admin API basic access: --" + webServiceAPIBasicAccess + "--");
|
||||
|
||||
SEBServerInit.INIT_LOGGER.info("---->");
|
||||
SEBServerInit.INIT_LOGGER.info("----> *********************************************************");
|
||||
SEBServerInit.INIT_LOGGER.info("----> *** GUI Service successfully successfully started up! ***");
|
||||
SEBServerInit.INIT_LOGGER.info("----> *********************************************************");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -60,6 +60,8 @@ public class WebserviceInfo {
|
|||
private final boolean isDistributed;
|
||||
private final String webserviceUUID;
|
||||
|
||||
private final long distributedPingUpdateInterval;
|
||||
|
||||
private Map<String, String> lmsExternalAddressAlias;
|
||||
|
||||
public WebserviceInfo(final Environment environment) {
|
||||
|
@ -74,6 +76,11 @@ public class WebserviceInfo {
|
|||
this.discoveryEndpoint = environment.getRequiredProperty(WEB_SERVICE_EXAM_API_DISCOVERY_ENDPOINT_KEY);
|
||||
this.contextPath = environment.getProperty(WEB_SERVICE_CONTEXT_PATH, "");
|
||||
|
||||
this.distributedPingUpdateInterval = environment.getProperty(
|
||||
"sebserver.webservice.distributed.pingUpdate",
|
||||
Long.class,
|
||||
3000L);
|
||||
|
||||
if (StringUtils.isEmpty(this.webserverName)) {
|
||||
log.warn("NOTE: External server name, property : 'sebserver.webservice.http.external.servername' "
|
||||
+ "is not set from configuration. The external server name is set to the server address!");
|
||||
|
@ -160,6 +167,10 @@ public class WebserviceInfo {
|
|||
return this.serverURLPrefix + this.discoveryEndpoint;
|
||||
}
|
||||
|
||||
public long getDistributedPingUpdateInterval() {
|
||||
return this.distributedPingUpdateInterval;
|
||||
}
|
||||
|
||||
public String getLocalHostName() {
|
||||
try {
|
||||
return InetAddress.getLocalHost().getHostName();
|
||||
|
|
|
@ -15,6 +15,7 @@ import javax.annotation.PreDestroy;
|
|||
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
@ -31,6 +32,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.WebserviceInfoDAO;
|
|||
@Import(DataSourceAutoConfiguration.class)
|
||||
public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent> {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
private final SEBServerInit sebServerInit;
|
||||
private final Environment environment;
|
||||
private final WebserviceInfo webserviceInfo;
|
||||
|
@ -41,20 +43,26 @@ public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent
|
|||
|
||||
protected WebserviceInit(
|
||||
final SEBServerInit sebServerInit,
|
||||
final Environment environment,
|
||||
final WebserviceInfo webserviceInfo,
|
||||
final AdminUserInitializer adminUserInitializer,
|
||||
final ApplicationEventPublisher applicationEventPublisher,
|
||||
final WebserviceInfoDAO webserviceInfoDAO,
|
||||
final DBIntegrityChecker dbIntegrityChecker) {
|
||||
final DBIntegrityChecker dbIntegrityChecker,
|
||||
final ApplicationContext applicationContext) {
|
||||
|
||||
this.applicationContext = applicationContext;
|
||||
this.sebServerInit = sebServerInit;
|
||||
this.environment = environment;
|
||||
this.environment = applicationContext.getEnvironment();
|
||||
this.webserviceInfo = webserviceInfo;
|
||||
this.adminUserInitializer = adminUserInitializer;
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
this.webserviceInfoDAO = webserviceInfoDAO;
|
||||
this.dbIntegrityChecker = dbIntegrityChecker;
|
||||
|
||||
}
|
||||
|
||||
public ApplicationContext getApplicationContext() {
|
||||
return this.applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -62,17 +70,21 @@ public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent
|
|||
|
||||
this.sebServerInit.init();
|
||||
|
||||
SEBServerInit.INIT_LOGGER.info("----> **** Webservice starting up... ****");
|
||||
|
||||
SEBServerInit.INIT_LOGGER.info("----> *********************************************************");
|
||||
SEBServerInit.INIT_LOGGER.info("----> *** Webservice starting up... ***");
|
||||
SEBServerInit.INIT_LOGGER.info("----> *********************************************************");
|
||||
SEBServerInit.INIT_LOGGER.info("----> ");
|
||||
SEBServerInit.INIT_LOGGER.info("----> Register Webservice: {}", this.webserviceInfo.getWebserviceUUID());
|
||||
|
||||
try {
|
||||
this.webserviceInfoDAO.register(
|
||||
final boolean register = this.webserviceInfoDAO.register(
|
||||
this.webserviceInfo.getWebserviceUUID(),
|
||||
InetAddress.getLocalHost().getHostAddress());
|
||||
if (register) {
|
||||
SEBServerInit.INIT_LOGGER.info("----> Successfully register Webservice instance");
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
SEBServerInit.INIT_LOGGER.error("Failed to register webservice: ", e);
|
||||
SEBServerInit.INIT_LOGGER.error("----> Failed to register webservice: ", e);
|
||||
}
|
||||
|
||||
SEBServerInit.INIT_LOGGER.info("----> ");
|
||||
|
@ -81,23 +93,31 @@ public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent
|
|||
|
||||
this.applicationEventPublisher.publishEvent(new SEBServerInitEvent(this));
|
||||
|
||||
SEBServerInit.INIT_LOGGER.info("----> ");
|
||||
SEBServerInit.INIT_LOGGER.info("----> **** Webservice successfully started up! **** ");
|
||||
// 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();
|
||||
|
||||
SEBServerInit.INIT_LOGGER.info("----> *********************************************************");
|
||||
SEBServerInit.INIT_LOGGER.info("----> *** Webservice Info: ***");
|
||||
SEBServerInit.INIT_LOGGER.info("----> *********************************************************");
|
||||
SEBServerInit.INIT_LOGGER.info("---->");
|
||||
SEBServerInit.INIT_LOGGER.info("----> *** Info:");
|
||||
|
||||
SEBServerInit.INIT_LOGGER.info("----> JDBC connection pool max size: {}",
|
||||
this.environment.getProperty("spring.datasource.hikari.maximumPoolSize"));
|
||||
|
||||
if (this.webserviceInfo.isDistributed()) {
|
||||
SEBServerInit.INIT_LOGGER.info("----> ");
|
||||
SEBServerInit.INIT_LOGGER.info("----> Distributed Setup: {}", this.webserviceInfo.getWebserviceUUID());
|
||||
SEBServerInit.INIT_LOGGER.info("------> Ping update time: {}",
|
||||
SEBServerInit.INIT_LOGGER.info("----> Ping update time: {}",
|
||||
this.environment.getProperty("sebserver.webservice.distributed.pingUpdate"));
|
||||
SEBServerInit.INIT_LOGGER.info("------> Connection update time: {}",
|
||||
SEBServerInit.INIT_LOGGER.info("----> Connection update time: {}",
|
||||
this.environment.getProperty("sebserver.webservice.distributed.connectionUpdate", "2000"));
|
||||
}
|
||||
|
||||
try {
|
||||
SEBServerInit.INIT_LOGGER.info("----> ");
|
||||
SEBServerInit.INIT_LOGGER.info("----> Server address: {}", this.environment.getProperty("server.address"));
|
||||
SEBServerInit.INIT_LOGGER.info("----> Server port: {}", this.environment.getProperty("server.port"));
|
||||
SEBServerInit.INIT_LOGGER.info("---->");
|
||||
|
@ -122,19 +142,18 @@ 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();
|
||||
SEBServerInit.INIT_LOGGER.info("---->");
|
||||
SEBServerInit.INIT_LOGGER.info("----> *********************************************************");
|
||||
SEBServerInit.INIT_LOGGER.info("----> *** Webservice successfully started up! ***");
|
||||
SEBServerInit.INIT_LOGGER.info("----> *********************************************************");
|
||||
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void gracefulShutdown() {
|
||||
SEBServerInit.INIT_LOGGER.info("**** Gracefully Shutdown of SEB Server instance {} ****",
|
||||
SEBServerInit.INIT_LOGGER.info("*********************************************************");
|
||||
SEBServerInit.INIT_LOGGER.info("**** Gracefully Shutdown of SEB Server instance {}",
|
||||
this.webserviceInfo.getHostAddress());
|
||||
|
||||
SEBServerInit.INIT_LOGGER.info("---->");
|
||||
SEBServerInit.INIT_LOGGER.info("----> Unregister Webservice: {}", this.webserviceInfo.getWebserviceUUID());
|
||||
|
||||
|
|
|
@ -50,12 +50,12 @@ public interface ClientPingMapper {
|
|||
int update(UpdateStatementProvider updateStatement);
|
||||
|
||||
@SelectProvider(type = SqlProviderAdapter.class, method = "select")
|
||||
@ResultType(ClientEventLastPingRecord.class)
|
||||
@ResultType(ClientLastPingRecord.class)
|
||||
@ConstructorArgs({
|
||||
@Arg(column = "id", javaType = Long.class, jdbcType = JdbcType.BIGINT),
|
||||
@Arg(column = "value", javaType = Long.class, jdbcType = JdbcType.BIGINT)
|
||||
})
|
||||
Collection<ClientEventLastPingRecord> selectMany(SelectStatementProvider select);
|
||||
Collection<ClientLastPingRecord> selectMany(SelectStatementProvider select);
|
||||
|
||||
default Long selectPingTimeByPrimaryKey(final Long id_) {
|
||||
return SelectDSL.selectWithMapper(
|
||||
|
@ -78,7 +78,7 @@ public interface ClientPingMapper {
|
|||
.execute();
|
||||
}
|
||||
|
||||
default QueryExpressionDSL<MyBatis3SelectModelAdapter<Collection<ClientEventLastPingRecord>>> selectByExample() {
|
||||
default QueryExpressionDSL<MyBatis3SelectModelAdapter<Collection<ClientLastPingRecord>>> selectByExample() {
|
||||
|
||||
return SelectDSL.selectWithMapper(
|
||||
this::selectMany,
|
||||
|
@ -95,12 +95,12 @@ public interface ClientPingMapper {
|
|||
.execute();
|
||||
}
|
||||
|
||||
final class ClientEventLastPingRecord {
|
||||
final class ClientLastPingRecord {
|
||||
|
||||
public final Long id;
|
||||
public final Long lastPingTime;
|
||||
|
||||
public ClientEventLastPingRecord(
|
||||
public ClientLastPingRecord(
|
||||
final Long id,
|
||||
final Long value) {
|
||||
|
||||
|
|
|
@ -41,6 +41,8 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionR
|
|||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionRecordMapper;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordDynamicSqlSupport;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordMapper;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientIndicatorRecordDynamicSqlSupport;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientIndicatorRecordMapper;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientInstructionRecordDynamicSqlSupport;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientInstructionRecordMapper;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamRecordDynamicSqlSupport;
|
||||
|
@ -63,15 +65,18 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
|
|||
private final ClientConnectionRecordMapper clientConnectionRecordMapper;
|
||||
private final ClientEventRecordMapper clientEventRecordMapper;
|
||||
private final ClientInstructionRecordMapper clientInstructionRecordMapper;
|
||||
private final ClientIndicatorRecordMapper clientIndicatorRecordMapper;
|
||||
|
||||
protected ClientConnectionDAOImpl(
|
||||
final ClientConnectionRecordMapper clientConnectionRecordMapper,
|
||||
final ClientEventRecordMapper clientEventRecordMapper,
|
||||
final ClientInstructionRecordMapper clientInstructionRecordMapper) {
|
||||
final ClientInstructionRecordMapper clientInstructionRecordMapper,
|
||||
final ClientIndicatorRecordMapper clientIndicatorRecordMapper) {
|
||||
|
||||
this.clientConnectionRecordMapper = clientConnectionRecordMapper;
|
||||
this.clientEventRecordMapper = clientEventRecordMapper;
|
||||
this.clientInstructionRecordMapper = clientInstructionRecordMapper;
|
||||
this.clientIndicatorRecordMapper = clientIndicatorRecordMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -474,7 +479,15 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
|
|||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// first delete all related client events
|
||||
// delete all related client indicators
|
||||
this.clientIndicatorRecordMapper.deleteByExample()
|
||||
.where(
|
||||
ClientIndicatorRecordDynamicSqlSupport.clientConnectionId,
|
||||
SqlBuilder.isIn(ids))
|
||||
.build()
|
||||
.execute();
|
||||
|
||||
// delete all related client events
|
||||
this.clientEventRecordMapper.deleteByExample()
|
||||
.where(
|
||||
ClientEventRecordDynamicSqlSupport.clientConnectionId,
|
||||
|
|
|
@ -237,18 +237,19 @@ public class ExamAdminServiceImpl implements ExamAdminService {
|
|||
|
||||
@Override
|
||||
public Result<Boolean> isProctoringEnabled(final Long examId) {
|
||||
final Result<Boolean> result = this.additionalAttributesDAO.getAdditionalAttribute(
|
||||
return this.additionalAttributesDAO.getAdditionalAttribute(
|
||||
EntityType.EXAM,
|
||||
examId,
|
||||
ProctoringServiceSettings.ATTR_ENABLE_PROCTORING)
|
||||
.map(rec -> BooleanUtils.toBoolean(rec.getValue()))
|
||||
.onError(error -> log.warn("Failed to verify proctoring enabled for exam: {}, {}",
|
||||
examId,
|
||||
error.getMessage()));
|
||||
if (result.hasError()) {
|
||||
return Result.of(false);
|
||||
}
|
||||
return result;
|
||||
.onErrorDo(error -> {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.warn("Failed to verify proctoring enabled for exam: {}, {}",
|
||||
examId,
|
||||
error.getMessage());
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,6 +15,7 @@ import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
|
|||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.IndicatorValue;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator.ClientIndicatorType;
|
||||
|
||||
/** A client indicator is a indicator value holder for a specific Indicator
|
||||
* on a running client connection.
|
||||
|
@ -31,6 +32,11 @@ public interface ClientIndicator extends IndicatorValue {
|
|||
* @param cachingEnabled defines whether indicator value caching is enabled or not. */
|
||||
void init(Indicator indicatorDefinition, Long connectionId, boolean active, boolean cachingEnabled);
|
||||
|
||||
/** Get the client indicator type
|
||||
*
|
||||
* @return the client indicator type */
|
||||
ClientIndicatorType indicatorType();
|
||||
|
||||
/** Get the exam identifier of the client connection of this ClientIndicator
|
||||
*
|
||||
* @return the exam identifier of the client connection of this ClientIndicator */
|
||||
|
|
|
@ -44,7 +44,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService;
|
|||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientConnectionService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientInstructionService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientNotificationService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator.DistributedPingCache;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator.DistributedPingService;
|
||||
import ch.ethz.seb.sebserver.webservice.weblayer.api.APIConstraintViolationException;
|
||||
|
||||
@Lazy
|
||||
|
@ -70,7 +70,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
|
|||
private final SEBClientInstructionService sebInstructionService;
|
||||
private final SEBClientNotificationService sebClientNotificationService;
|
||||
private final ExamAdminService examAdminService;
|
||||
private final DistributedPingCache distributedPingCache;
|
||||
private final DistributedPingService distributedPingCache;
|
||||
private final boolean isDistributedSetup;
|
||||
|
||||
protected SEBClientConnectionServiceImpl(
|
||||
|
@ -80,7 +80,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
|
|||
final SEBClientInstructionService sebInstructionService,
|
||||
final SEBClientNotificationService sebClientNotificationService,
|
||||
final ExamAdminService examAdminService,
|
||||
final DistributedPingCache distributedPingCache) {
|
||||
final DistributedPingService distributedPingCache) {
|
||||
|
||||
this.examSessionService = examSessionService;
|
||||
this.examSessionCacheService = examSessionService.getExamSessionCacheService();
|
||||
|
|
|
@ -11,18 +11,14 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator;
|
|||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.ClientPingMapper;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator.DistributedPingService.PingUpdate;
|
||||
|
||||
public abstract class AbstractPingIndicator extends AbstractClientIndicator {
|
||||
|
||||
|
@ -30,17 +26,11 @@ public abstract class AbstractPingIndicator extends AbstractClientIndicator {
|
|||
|
||||
private final Set<EventType> EMPTY_SET = Collections.unmodifiableSet(EnumSet.noneOf(EventType.class));
|
||||
|
||||
private final Executor executor;
|
||||
protected final DistributedPingCache distributedPingCache;
|
||||
|
||||
protected final DistributedPingService distributedPingCache;
|
||||
protected PingUpdate pingUpdate = null;
|
||||
|
||||
protected AbstractPingIndicator(
|
||||
final DistributedPingCache distributedPingCache,
|
||||
@Qualifier(AsyncServiceSpringConfig.EXAM_API_PING_SERVICE_EXECUTOR_BEAN_NAME) final Executor executor) {
|
||||
|
||||
protected AbstractPingIndicator(final DistributedPingService distributedPingCache) {
|
||||
super();
|
||||
this.executor = executor;
|
||||
this.distributedPingCache = distributedPingCache;
|
||||
}
|
||||
|
||||
|
@ -55,9 +45,9 @@ public abstract class AbstractPingIndicator extends AbstractClientIndicator {
|
|||
|
||||
if (!this.cachingEnabled && this.active) {
|
||||
try {
|
||||
createPingUpdate();
|
||||
this.pingUpdate = this.distributedPingCache.createPingUpdate(connectionId);
|
||||
} catch (final Exception e) {
|
||||
createPingUpdate();
|
||||
this.pingUpdate = this.distributedPingCache.createPingUpdate(connectionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,15 +64,7 @@ public abstract class AbstractPingIndicator extends AbstractClientIndicator {
|
|||
}
|
||||
}
|
||||
|
||||
// Update last ping time on persistent storage asynchronously within a defines thread pool with no
|
||||
// waiting queue to skip further ping updates if all update threads are busy
|
||||
try {
|
||||
this.executor.execute(this.pingUpdate);
|
||||
} catch (final Exception e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.warn("Failed to schedule ping task: {}" + e.getMessage());
|
||||
}
|
||||
}
|
||||
this.distributedPingCache.updatePingAsync(this.pingUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,21 +75,15 @@ public abstract class AbstractPingIndicator extends AbstractClientIndicator {
|
|||
}
|
||||
|
||||
try {
|
||||
createPingUpdate();
|
||||
this.pingUpdate = this.distributedPingCache.createPingUpdate(this.connectionId);
|
||||
if (this.pingUpdate == null) {
|
||||
createPingUpdate();
|
||||
this.pingUpdate = this.distributedPingCache.createPingUpdate(this.connectionId);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to recover ping record for connection: {}", this.connectionId, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void createPingUpdate() {
|
||||
this.pingUpdate = new PingUpdate(
|
||||
this.distributedPingCache.getClientPingMapper(),
|
||||
this.distributedPingCache.initPingForConnection(this.connectionId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<EventType> observedEvents() {
|
||||
return this.EMPTY_SET;
|
||||
|
@ -115,26 +91,4 @@ public abstract class AbstractPingIndicator extends AbstractClientIndicator {
|
|||
|
||||
public abstract ClientEventRecord updateLogEvent(final long now);
|
||||
|
||||
static final class PingUpdate implements Runnable {
|
||||
|
||||
private final ClientPingMapper clientPingMapper;
|
||||
final Long pingRecord;
|
||||
|
||||
public PingUpdate(final ClientPingMapper clientPingMapper, final Long pingRecord) {
|
||||
this.clientPingMapper = clientPingMapper;
|
||||
this.pingRecord = pingRecord;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
this.clientPingMapper
|
||||
.updatePingTime(this.pingRecord, Utils.getMillisecondsNow());
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to update ping: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -45,4 +45,9 @@ public class BatteryStatusIndicator extends AbstractLogNumberIndicator {
|
|||
return IndicatorType.BATTERY_STATUS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientIndicatorType indicatorType() {
|
||||
return ClientIndicatorType.BATTERY_STATUS;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import static org.mybatis.dynamic.sql.SqlBuilder.isEqualTo;
|
|||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -20,50 +21,78 @@ import org.joda.time.DateTimeUtils;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import ch.ethz.seb.sebserver.SEBServerInit;
|
||||
import ch.ethz.seb.sebserver.SEBServerInitEvent;
|
||||
import ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.WebserviceInfo;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.ClientPingMapper;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.ClientPingMapper.ClientEventLastPingRecord;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordDynamicSqlSupport;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.ClientPingMapper.ClientLastPingRecord;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientIndicatorRecordDynamicSqlSupport;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientIndicatorRecordMapper;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientIndicatorRecord;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@WebServiceProfile
|
||||
public class DistributedPingCache implements DisposableBean {
|
||||
public class DistributedPingService implements DisposableBean {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(DistributedPingCache.class);
|
||||
private static final Logger log = LoggerFactory.getLogger(DistributedPingService.class);
|
||||
|
||||
private final Executor pingUpdateExecutor;
|
||||
private final ClientIndicatorRecordMapper clientIndicatorRecordMapper;
|
||||
private final ClientPingMapper clientPingMapper;
|
||||
private final long pingUpdateTolerance;
|
||||
private long pingUpdateTolerance;
|
||||
|
||||
private ScheduledFuture<?> taskRef;
|
||||
private final Map<Long, Long> pingCache = new ConcurrentHashMap<>();
|
||||
private long lastUpdate = 0L;
|
||||
|
||||
public DistributedPingCache(
|
||||
public DistributedPingService(
|
||||
@Qualifier(AsyncServiceSpringConfig.EXAM_API_PING_SERVICE_EXECUTOR_BEAN_NAME) final Executor pingUpdateExecutor,
|
||||
final ClientIndicatorRecordMapper clientIndicatorRecordMapper,
|
||||
final ClientPingMapper clientPingMapper,
|
||||
final WebserviceInfo webserviceInfo,
|
||||
final TaskScheduler taskScheduler,
|
||||
@Value("${sebserver.webservice.distributed.pingUpdate:3000}") final long pingUpdate) {
|
||||
final ClientPingMapper clientPingMapper) {
|
||||
|
||||
this.pingUpdateExecutor = pingUpdateExecutor;
|
||||
this.clientIndicatorRecordMapper = clientIndicatorRecordMapper;
|
||||
this.clientPingMapper = clientPingMapper;
|
||||
this.pingUpdateTolerance = pingUpdate * 2 / 3;
|
||||
}
|
||||
|
||||
@EventListener(SEBServerInitEvent.class)
|
||||
public void init(final SEBServerInitEvent initEvent) {
|
||||
final ApplicationContext applicationContext = initEvent.webserviceInit.getApplicationContext();
|
||||
final WebserviceInfo webserviceInfo = applicationContext.getBean(WebserviceInfo.class);
|
||||
if (webserviceInfo.isDistributed()) {
|
||||
|
||||
SEBServerInit.INIT_LOGGER.info("------>");
|
||||
SEBServerInit.INIT_LOGGER.info("------> Activate distributed ping service:");
|
||||
|
||||
final TaskScheduler taskScheduler = applicationContext.getBean(TaskScheduler.class);
|
||||
final long distributedPingUpdateInterval = webserviceInfo.getDistributedPingUpdateInterval();
|
||||
this.pingUpdateTolerance = distributedPingUpdateInterval * 2 / 3;
|
||||
|
||||
SEBServerInit.INIT_LOGGER.info("------> with distributedPingUpdateInterval: {}",
|
||||
distributedPingUpdateInterval);
|
||||
SEBServerInit.INIT_LOGGER.info("------> with taskScheduler: {}", taskScheduler);
|
||||
|
||||
try {
|
||||
this.taskRef = taskScheduler.scheduleAtFixedRate(this::updatePings, pingUpdate);
|
||||
this.taskRef = taskScheduler.scheduleAtFixedRate(
|
||||
this::persistentPingUpdate,
|
||||
distributedPingUpdateInterval);
|
||||
|
||||
SEBServerInit.INIT_LOGGER.info("------> distributed ping service successfully initialized!");
|
||||
|
||||
} catch (final Exception e) {
|
||||
SEBServerInit.INIT_LOGGER.error("------> Failed to initialize distributed ping service:", e);
|
||||
log.error("Failed to initialize distributed ping cache update task");
|
||||
this.taskRef = null;
|
||||
}
|
||||
|
@ -141,10 +170,10 @@ public class DistributedPingCache implements DisposableBean {
|
|||
log.debug("*** Delete ping record for SEB connection: {}", connectionId);
|
||||
}
|
||||
|
||||
final Collection<ClientEventLastPingRecord> records = this.clientPingMapper
|
||||
final Collection<ClientLastPingRecord> records = this.clientPingMapper
|
||||
.selectByExample()
|
||||
.where(ClientEventRecordDynamicSqlSupport.clientConnectionId, isEqualTo(connectionId))
|
||||
.and(ClientEventRecordDynamicSqlSupport.type, isEqualTo(ClientIndicatorType.LAST_PING.id))
|
||||
.where(ClientIndicatorRecordDynamicSqlSupport.clientConnectionId, isEqualTo(connectionId))
|
||||
.and(ClientIndicatorRecordDynamicSqlSupport.type, isEqualTo(ClientIndicatorType.LAST_PING.id))
|
||||
.build()
|
||||
.execute();
|
||||
|
||||
|
@ -194,8 +223,7 @@ public class DistributedPingCache implements DisposableBean {
|
|||
}
|
||||
}
|
||||
|
||||
private void updatePings() {
|
||||
|
||||
public void persistentPingUpdate() {
|
||||
if (this.pingCache.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -215,7 +243,7 @@ public class DistributedPingCache implements DisposableBean {
|
|||
final Map<Long, Long> mapping = this.clientPingMapper
|
||||
.selectByExample()
|
||||
.where(
|
||||
ClientEventRecordDynamicSqlSupport.type,
|
||||
ClientIndicatorRecordDynamicSqlSupport.type,
|
||||
isEqualTo(ClientIndicatorType.LAST_PING.id))
|
||||
.build()
|
||||
.execute()
|
||||
|
@ -235,6 +263,45 @@ public class DistributedPingCache implements DisposableBean {
|
|||
this.lastUpdate = millisecondsNow;
|
||||
}
|
||||
|
||||
// Update last ping time on persistent storage asynchronously within a defines thread pool with no
|
||||
// waiting queue to skip further ping updates if all update threads are busy
|
||||
void updatePingAsync(final PingUpdate pingUpdate) {
|
||||
try {
|
||||
this.pingUpdateExecutor.execute(pingUpdate);
|
||||
} catch (final Exception e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.warn("Failed to schedule ping task: {}" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PingUpdate createPingUpdate(final Long connectionId) {
|
||||
return new PingUpdate(
|
||||
this.getClientPingMapper(),
|
||||
this.initPingForConnection(connectionId));
|
||||
}
|
||||
|
||||
static final class PingUpdate implements Runnable {
|
||||
|
||||
private final ClientPingMapper clientPingMapper;
|
||||
final Long pingRecord;
|
||||
|
||||
public PingUpdate(final ClientPingMapper clientPingMapper, final Long pingRecord) {
|
||||
this.clientPingMapper = clientPingMapper;
|
||||
this.pingRecord = pingRecord;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
this.clientPingMapper
|
||||
.updatePingTime(this.pingRecord, Utils.getMillisecondsNow());
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to update ping: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
if (this.taskRef != null) {
|
|
@ -31,4 +31,9 @@ public final class ErrorLogCountClientIndicator extends AbstractLogLevelCountInd
|
|||
return IndicatorType.ERROR_COUNT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientIndicatorType indicatorType() {
|
||||
return ClientIndicatorType.ERROR_LOG_COUNT;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,4 +31,9 @@ public class InfoLogCountClientIndicator extends AbstractLogLevelCountIndicator
|
|||
return IndicatorType.INFO_COUNT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientIndicatorType indicatorType() {
|
||||
return ClientIndicatorType.INFO_LOG_COUNT;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,12 +10,10 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator;
|
|||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Comparator;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.joda.time.DateTimeUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
@ -24,7 +22,6 @@ import org.springframework.stereotype.Component;
|
|||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
|
||||
|
@ -47,10 +44,8 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator {
|
|||
private boolean missingPing = false;
|
||||
private boolean hidden = false;
|
||||
|
||||
public PingIntervalClientIndicator(
|
||||
final DistributedPingCache distributedPingCache,
|
||||
@Qualifier(AsyncServiceSpringConfig.EXAM_API_PING_SERVICE_EXECUTOR_BEAN_NAME) final Executor executor) {
|
||||
super(distributedPingCache, executor);
|
||||
public PingIntervalClientIndicator(final DistributedPingService distributedPingCache) {
|
||||
super(distributedPingCache);
|
||||
this.cachingEnabled = true;
|
||||
}
|
||||
|
||||
|
@ -89,6 +84,11 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientIndicatorType indicatorType() {
|
||||
return ClientIndicatorType.LAST_PING;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public final boolean isMissingPing() {
|
||||
return this.missingPing;
|
||||
|
|
|
@ -45,4 +45,9 @@ public class WLANStatusIndicator extends AbstractLogNumberIndicator {
|
|||
return IndicatorType.WLAN_STATUS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientIndicatorType indicatorType() {
|
||||
return ClientIndicatorType.WLAN_STATUS;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,4 +30,9 @@ public class WarnLogCountClientIndicator extends AbstractLogLevelCountIndicator
|
|||
public IndicatorType getType() {
|
||||
return IndicatorType.WARN_COUNT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientIndicatorType indicatorType() {
|
||||
return ClientIndicatorType.WARN_LOG_COUNT;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,6 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator;
|
|||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.joda.time.DateTimeUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
@ -20,7 +18,6 @@ import org.mockito.Mockito;
|
|||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientEventDAO;
|
||||
|
||||
public class PingIntervalClientIndicatorTest {
|
||||
|
||||
|
@ -34,12 +31,10 @@ public class PingIntervalClientIndicatorTest {
|
|||
|
||||
DateTimeUtils.setCurrentMillisProvider(() -> 1L);
|
||||
|
||||
final ClientEventDAO clientEventDAO = Mockito.mock(ClientEventDAO.class);
|
||||
final DistributedPingCache distributedPingCache = Mockito.mock(DistributedPingCache.class);
|
||||
final Executor executor = Mockito.mock(Executor.class);
|
||||
final DistributedPingService distributedPingCache = Mockito.mock(DistributedPingService.class);
|
||||
|
||||
final PingIntervalClientIndicator pingIntervalClientIndicator =
|
||||
new PingIntervalClientIndicator(distributedPingCache, executor);
|
||||
new PingIntervalClientIndicator(distributedPingCache);
|
||||
assertEquals("0.0", String.valueOf(pingIntervalClientIndicator.getValue()));
|
||||
}
|
||||
|
||||
|
@ -48,12 +43,10 @@ public class PingIntervalClientIndicatorTest {
|
|||
|
||||
DateTimeUtils.setCurrentMillisProvider(() -> 1L);
|
||||
|
||||
final ClientEventDAO clientEventDAO = Mockito.mock(ClientEventDAO.class);
|
||||
final DistributedPingCache distributedPingCache = Mockito.mock(DistributedPingCache.class);
|
||||
final Executor executor = Mockito.mock(Executor.class);
|
||||
final DistributedPingService distributedPingCache = Mockito.mock(DistributedPingService.class);
|
||||
|
||||
final PingIntervalClientIndicator pingIntervalClientIndicator =
|
||||
new PingIntervalClientIndicator(distributedPingCache, executor);
|
||||
new PingIntervalClientIndicator(distributedPingCache);
|
||||
assertEquals("0.0", String.valueOf(pingIntervalClientIndicator.getValue()));
|
||||
|
||||
DateTimeUtils.setCurrentMillisProvider(() -> 10L);
|
||||
|
@ -65,12 +58,10 @@ public class PingIntervalClientIndicatorTest {
|
|||
public void testSerialization() throws JsonProcessingException {
|
||||
DateTimeUtils.setCurrentMillisProvider(() -> 1L);
|
||||
|
||||
final ClientEventDAO clientEventDAO = Mockito.mock(ClientEventDAO.class);
|
||||
final DistributedPingCache distributedPingCache = Mockito.mock(DistributedPingCache.class);
|
||||
final Executor executor = Mockito.mock(Executor.class);
|
||||
final DistributedPingService distributedPingCache = Mockito.mock(DistributedPingService.class);
|
||||
|
||||
final PingIntervalClientIndicator pingIntervalClientIndicator =
|
||||
new PingIntervalClientIndicator(distributedPingCache, executor);
|
||||
new PingIntervalClientIndicator(distributedPingCache);
|
||||
final JSONMapper jsonMapper = new JSONMapper();
|
||||
final String json = jsonMapper.writeValueAsString(pingIntervalClientIndicator);
|
||||
assertEquals("{\"indicatorValue\":0.0,\"indicatorType\":\"LAST_PING\"}", json);
|
||||
|
|
Loading…
Reference in a new issue