diff --git a/source/src/main/java/org/cerberus/core/apiprivate/ExecutionPrivateController.java b/source/src/main/java/org/cerberus/core/apiprivate/ExecutionPrivateController.java index 7c05c7066e..ea89f6d2ac 100644 --- a/source/src/main/java/org/cerberus/core/apiprivate/ExecutionPrivateController.java +++ b/source/src/main/java/org/cerberus/core/apiprivate/ExecutionPrivateController.java @@ -55,7 +55,7 @@ public class ExecutionPrivateController { TestCaseExecutionService executionService; @Autowired - ExecutionUUID executionUUIDObject; + private ExecutionUUID executionUUIDObject; @GetMapping("/getLastByCriteria") public String getLastByCriteria( @@ -110,9 +110,7 @@ public String getRunning( try { // ApplicationContext appContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext()); - LOG.debug("TOTO"); LOG.debug(executionUUIDObject.getExecutionUUIDList()); - jsonResponse.put("simultaneous_execution", executionUUIDObject.size()); JSONArray executionArray = new JSONArray(); for (Object ex : executionUUIDObject.getExecutionUUIDList().values()) { TestCaseExecution execution = (TestCaseExecution) ex; @@ -129,7 +127,13 @@ public String getRunning( object.put("start", new Timestamp(execution.getStart())); executionArray.put(object); } - jsonResponse.put("simultaneous_execution_list", executionArray); + jsonResponse.put("runningExecutionsList", executionArray); + + JSONObject queueStatus = new JSONObject(); + queueStatus.put("queueSize", executionUUIDObject.getQueueSize()); + queueStatus.put("globalLimit", executionUUIDObject.getGlobalLimit()); + queueStatus.put("running", executionUUIDObject.getRunning()); + jsonResponse.put("queueStats", queueStatus); return jsonResponse.toString(); diff --git a/source/src/main/java/org/cerberus/core/crud/entity/TestCaseExecution.java b/source/src/main/java/org/cerberus/core/crud/entity/TestCaseExecution.java index 935525f700..0c6b1038d1 100644 --- a/source/src/main/java/org/cerberus/core/crud/entity/TestCaseExecution.java +++ b/source/src/main/java/org/cerberus/core/crud/entity/TestCaseExecution.java @@ -167,6 +167,7 @@ public class TestCaseExecution { private Integer nbExecutions; // Has the nb of execution that was necessary to execute the testcase. // Global parameters. private Integer cerberus_action_wait_default; + // Websocket management parameters private boolean cerberus_featureflipping_activatewebsocketpush; private long cerberus_featureflipping_websocketpushperiod; private long lastWebsocketPush; diff --git a/source/src/main/java/org/cerberus/core/engine/entity/ExecutionUUID.java b/source/src/main/java/org/cerberus/core/engine/entity/ExecutionUUID.java index 3c84963e6c..c42d5b8cda 100644 --- a/source/src/main/java/org/cerberus/core/engine/entity/ExecutionUUID.java +++ b/source/src/main/java/org/cerberus/core/engine/entity/ExecutionUUID.java @@ -32,10 +32,34 @@ public class ExecutionUUID { private HashMap executionHashMap; + private int running; + private int queueSize; + private int globalLimit; + + public int getRunning() { + return running; + } + + public int getQueueSize() { + return queueSize; + } + + public int getGlobalLimit() { + return globalLimit; + } + + public void setQueueCounters(int globalLimit, int running, int queueSize) { + this.globalLimit = globalLimit; + this.running = running; + this.queueSize = queueSize; + } @PostConstruct public void init() { executionHashMap = new HashMap<>(); + running = 0; + queueSize = 0; + globalLimit = 0; } public HashMap getExecutionUUIDList() { diff --git a/source/src/main/java/org/cerberus/core/engine/execution/impl/ExecutionStartService.java b/source/src/main/java/org/cerberus/core/engine/execution/impl/ExecutionStartService.java index 0f1de9d036..1b8c8acad5 100644 --- a/source/src/main/java/org/cerberus/core/engine/execution/impl/ExecutionStartService.java +++ b/source/src/main/java/org/cerberus/core/engine/execution/impl/ExecutionStartService.java @@ -57,6 +57,8 @@ import org.cerberus.core.exception.CerberusException; import org.cerberus.core.util.ParameterParserUtil; import org.cerberus.core.util.StringUtil; +import org.cerberus.core.websocket.QueueStatus; +import org.cerberus.core.websocket.QueueStatusEndPoint; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -87,7 +89,7 @@ public class ExecutionStartService implements IExecutionStartService { @Autowired private IInvariantService invariantService; @Autowired - ExecutionUUID executionUUIDObject; + private ExecutionUUID executionUUIDObject; @Autowired private ITagService tagService; @Autowired @@ -564,6 +566,12 @@ public TestCaseExecution startExecution(TestCaseExecution execution) throws Cerb if (runID != 0) { execution.setId(runID); executionUUIDObject.setExecutionUUID(execution.getExecutionUUID(), execution); + QueueStatus queueS = QueueStatus.builder() + .executionHashMap(executionUUIDObject.getExecutionUUIDList()) + .globalLimit(executionUUIDObject.getGlobalLimit()) + .running(executionUUIDObject.getRunning()) + .queueSize(executionUUIDObject.getQueueSize()).build(); + QueueStatusEndPoint.getInstance().send(queueS, true); // Update Queue Execution here if QueueID =! 0. if (execution.getQueueID() != 0) { inQueueService.updateToExecuting(execution.getQueueID(), "", runID); diff --git a/source/src/main/java/org/cerberus/core/engine/queuemanagement/impl/ExecutionQueueThreadPool.java b/source/src/main/java/org/cerberus/core/engine/queuemanagement/impl/ExecutionQueueThreadPool.java index f62a891ee8..0dbf0c1d84 100644 --- a/source/src/main/java/org/cerberus/core/engine/queuemanagement/impl/ExecutionQueueThreadPool.java +++ b/source/src/main/java/org/cerberus/core/engine/queuemanagement/impl/ExecutionQueueThreadPool.java @@ -39,16 +39,16 @@ public class ExecutionQueueThreadPool { private static final org.apache.logging.log4j.Logger LOG = org.apache.logging.log4j.LogManager.getLogger(ExecutionQueueThreadPool.class); private ExecutorService executor; - private Integer totalNumberOfThread; - private Integer size; - private Integer inExecution; - Map>> map = new HashMap<>(); +// private Integer totalNumberOfThread; +// private Integer size; +// private Integer inExecution; +// Map>> map = new HashMap<>(); @PostConstruct public void init() { executor = Executors.newCachedThreadPool(); - totalNumberOfThread = 0; - inExecution = 0; +// totalNumberOfThread = 0; +// inExecution = 0; LOG.debug("Starting Execution Queueing !! (ExecutionQueueThreadPool)."); } @@ -61,22 +61,22 @@ public void setExecutor(ExecutorService executor) { } //Changer le nombre de tache simultanée... - public void setNumberOfPool(Integer numberOfPool) { - totalNumberOfThread = numberOfPool; - } - - public Integer getSize() { - return size; - } - - public Integer getNumberOfThread() { - return totalNumberOfThread; - } - - public void setSize(Integer size) { - this.size = size; - } - +// public void setNumberOfPool(Integer numberOfPool) { +// totalNumberOfThread = numberOfPool; +// } + +// public Integer getSize() { +// return size; +// } +// +// public Integer getNumberOfThread() { +// return totalNumberOfThread; +// } + +// public void setSize(Integer size) { +// this.size = size; +// } +// public void reset() { this.stop(); init(); @@ -88,20 +88,20 @@ public void stop() { } } - public Integer getInExecution() { - return inExecution; - } - - public void setInExecution(Integer inExecution) { - this.inExecution = inExecution; - } - - public void incrementInExecution() { - this.inExecution++; - } - - public void decrementInExecution() { - this.inExecution--; - } +// public Integer getInExecution() { +// return inExecution; +// } +// +// public void setInExecution(Integer inExecution) { +// this.inExecution = inExecution; +// } +// +// public void incrementInExecution() { +// this.inExecution++; +// } +// +// public void decrementInExecution() { +// this.inExecution--; +// } } diff --git a/source/src/main/java/org/cerberus/core/engine/queuemanagement/impl/ExecutionThreadPoolService.java b/source/src/main/java/org/cerberus/core/engine/queuemanagement/impl/ExecutionThreadPoolService.java index 6acb10142b..da5d292afe 100644 --- a/source/src/main/java/org/cerberus/core/engine/queuemanagement/impl/ExecutionThreadPoolService.java +++ b/source/src/main/java/org/cerberus/core/engine/queuemanagement/impl/ExecutionThreadPoolService.java @@ -43,6 +43,7 @@ import org.cerberus.core.crud.service.ITagService; import org.cerberus.core.crud.service.ITestCaseExecutionQueueDepService; import org.cerberus.core.crud.service.ITestCaseExecutionQueueService; +import org.cerberus.core.engine.entity.ExecutionUUID; import org.cerberus.core.engine.queuemanagement.IExecutionThreadPoolService; import org.cerberus.core.exception.CerberusException; import org.cerberus.core.service.authentification.impl.APIKeyService; @@ -51,6 +52,8 @@ import org.cerberus.core.util.ParameterParserUtil; import org.cerberus.core.util.StringUtil; import org.cerberus.core.util.answer.AnswerList; +import org.cerberus.core.websocket.QueueStatus; +import org.cerberus.core.websocket.QueueStatusEndPoint; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @@ -82,6 +85,8 @@ public class ExecutionThreadPoolService implements IExecutionThreadPoolService { @Autowired private IMyVersionService myVersionService; @Autowired + private ExecutionUUID executionUUIDObject; + @Autowired ExecutionQueueThreadPool threadQueuePool; @Autowired private ITestCaseExecutionQueueService queueService; @@ -662,6 +667,16 @@ public void executeNextInQueue(boolean forceExecution) throws CerberusException } LOG.debug("Stats : GlobalContrain=" + poolSizeGeneral + " - nbRunning=" + const01_current + " - NbQueued=" + executionsInQueue.size()); + if (nbqueuedexe == 0) { // Websocket of queue status is sent only if no new execution was submitted. In case a new execution is submitted, the websocket is refreshed only when execution has been created on database. + executionUUIDObject.setQueueCounters(poolSizeGeneral, const01_current, executionsInQueue.size()); + QueueStatus queueS = QueueStatus.builder() + .executionHashMap(executionUUIDObject.getExecutionUUIDList()) + .globalLimit(poolSizeGeneral) + .running(const01_current) + .queueSize(executionsInQueue.size()).build(); + QueueStatusEndPoint.getInstance().send(queueS, true); + } + queueStatService.create(factoryQueueStat.create(0, poolSizeGeneral, const01_current, executionsInQueue.size(), "", null, null, null)); } while (nbqueuedexe > 0); diff --git a/source/src/main/java/org/cerberus/core/servlet/information/ReadCerberusDetailInformation.java b/source/src/main/java/org/cerberus/core/servlet/information/ReadCerberusDetailInformation.java index 51ccbf29fa..7786102c40 100644 --- a/source/src/main/java/org/cerberus/core/servlet/information/ReadCerberusDetailInformation.java +++ b/source/src/main/java/org/cerberus/core/servlet/information/ReadCerberusDetailInformation.java @@ -99,7 +99,6 @@ protected void processRequest(HttpServletRequest request, HttpServletResponse re Infos infos = new Infos(); try { - jsonResponse.put("simultaneous_execution", euuid.size()); JSONArray executionArray = new JSONArray(); for (Object ex : euuid.getExecutionUUIDList().values()) { TestCaseExecution execution = (TestCaseExecution) ex; @@ -116,14 +115,15 @@ protected void processRequest(HttpServletRequest request, HttpServletResponse re object.put("start", new Timestamp(execution.getStart())); executionArray.put(object); } - jsonResponse.put("simultaneous_execution_list", executionArray); + jsonResponse.put("runningExecutionsList", executionArray); + jsonResponse.put("simultaneous_session", sc.getTotalActiveSession()); jsonResponse.put("active_users", sc.getActiveUsers()); - JSONObject object = new JSONObject(); + JSONObject schedulerObject = new JSONObject(); if (scInit != null) { - object.put("schedulerInstanceVersion", scInit.getInstanceSchedulerVersion()); - object.put("schedulerReloadIsRunning", scInit.isIsRunning()); + schedulerObject.put("schedulerInstanceVersion", scInit.getInstanceSchedulerVersion()); + schedulerObject.put("schedulerReloadIsRunning", scInit.isIsRunning()); // We get here the list of triggers of Quartz scheduler. List triggerList = new ArrayList<>(); for (Trigger triggerSet : scInit.getMyTriggersSet()) { @@ -138,12 +138,18 @@ protected void processRequest(HttpServletRequest request, HttpServletResponse re } Collections.sort(triggerList, new SortTriggers()); JSONArray object1 = new JSONArray(triggerList); - object.put("schedulerTriggers", object1); + schedulerObject.put("schedulerTriggers", object1); Date now = new Date(); - object.put("serverDate", new SimpleDateFormat(DATE_FORMAT).format(now)); - object.put("serverTimeZone", TimeZone.getDefault().getDisplayName()); + schedulerObject.put("serverDate", new SimpleDateFormat(DATE_FORMAT).format(now)); + schedulerObject.put("serverTimeZone", TimeZone.getDefault().getDisplayName()); } - jsonResponse.put("scheduler", object); + jsonResponse.put("scheduler", schedulerObject); + + JSONObject queueObject = new JSONObject(); + queueObject.put("globalLimit", euuid.getGlobalLimit()); + queueObject.put("running", euuid.getRunning()); + queueObject.put("queueSize", euuid.getQueueSize()); + jsonResponse.put("queueStats", queueObject); cerberusDatabaseInformation = appContext.getBean(ICerberusInformationDAO.class); diff --git a/source/src/main/java/org/cerberus/core/websocket/QueueStatus.java b/source/src/main/java/org/cerberus/core/websocket/QueueStatus.java new file mode 100644 index 0000000000..826596f7d4 --- /dev/null +++ b/source/src/main/java/org/cerberus/core/websocket/QueueStatus.java @@ -0,0 +1,156 @@ +/** + * Cerberus Copyright (C) 2013 - 2017 cerberustesting + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file is part of Cerberus. + * + * Cerberus is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Cerberus is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Cerberus. If not, see . + */ +package org.cerberus.core.websocket; + +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import javax.persistence.Entity; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.json.JSONException; +import org.json.JSONObject; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.cerberus.core.crud.entity.TestCaseExecution; +import org.json.JSONArray; + +/** + * @author bcivel + */ +@Getter +@Setter +@Builder +@EqualsAndHashCode +@AllArgsConstructor +@NoArgsConstructor +public class QueueStatus { + + // Websocket data content + private HashMap executionHashMap; + private int running; + private int queueSize; + private int globalLimit; + + private long lastWebsocketPush; + + /** + * Not included in table. + */ + private static final Logger LOG = LogManager.getLogger(QueueStatus.class); + + public void setQueueCounters(int globalLimit, int running, int queueSize) { + this.globalLimit = globalLimit; + this.running = running; + this.queueSize = queueSize; + } + + public HashMap getExecutionUUIDList() { + return executionHashMap; + } + + public void setExecutionUUID(String UUID, TestCaseExecution execution) { + executionHashMap.put(UUID, execution); + } + + public long getLastWebsocketPush() { + return lastWebsocketPush; + } + + public void setLastWebsocketPush(long lastWebsocketPush) { + this.lastWebsocketPush = lastWebsocketPush; + } + + public JSONObject toJson(boolean fatVersion) { + JSONObject queueJson = new JSONObject(); + try { + JSONObject queueStats = new JSONObject(); + queueStats.put("globalLimit", this.getGlobalLimit()); + queueStats.put("running", this.getRunning()); + queueStats.put("queueSize", this.getQueueSize()); + queueJson.put("queueStats", queueStats); + queueJson.put("queueTotal", executionHashMap.size()); + + List executionArray = new ArrayList<>(); +// JSONArray executionArray = new JSONArray(); + for (Object ex : executionHashMap.values()) { + TestCaseExecution execution = (TestCaseExecution) ex; + JSONObject object = new JSONObject(); + object.put("id", execution.getId()); + object.put("test", execution.getTest()); + object.put("testcase", execution.getTestCase()); + object.put("system", execution.getApplicationObj().getSystem()); + object.put("application", execution.getApplication()); + object.put("environment", execution.getEnvironmentData()); + object.put("country", execution.getCountry()); + object.put("robotIP", execution.getSeleniumIP()); + object.put("tag", execution.getTag()); + object.put("start", new Timestamp(execution.getStart())); + executionArray.add(object); + } + Collections.sort(executionArray, new SortExecutions()); + JSONArray object1 = new JSONArray(executionArray); + queueJson.put("runningExecutionsList", object1); + + //queueStats.queueSize + } catch (JSONException ex) { + LOG.error(ex.toString(), ex); + } + return queueJson; + } + + class SortExecutions implements Comparator { + + // Used for sorting Triggers + @Override + public int compare(JSONObject a, JSONObject b) { + + if (a != null && b != null) { + Date dateA; + Date dateB; + try { + dateA = (Date) a.get("start"); + dateB = (Date) b.get("start"); + if (dateA.equals(dateB)) { + + } else { + return (dateA.compareTo(dateB)); + } + } catch (JSONException ex) { + LOG.error("Exception on JSON Parse.", ex); + } + + } else { + return 1; + } + + return 1; + } + } + +} diff --git a/source/src/main/java/org/cerberus/core/websocket/QueueStatusEndPoint.java b/source/src/main/java/org/cerberus/core/websocket/QueueStatusEndPoint.java new file mode 100644 index 0000000000..3015836548 --- /dev/null +++ b/source/src/main/java/org/cerberus/core/websocket/QueueStatusEndPoint.java @@ -0,0 +1,273 @@ +/** + * Cerberus Copyright (C) 2013 - 2017 cerberustesting + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file is part of Cerberus. + * + * Cerberus is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Cerberus is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Cerberus. If not, see . + */ +package org.cerberus.core.websocket; + +import com.google.common.base.Predicates; +import com.google.common.collect.Maps; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import javax.websocket.EndpointConfig; +import javax.websocket.OnClose; +import javax.websocket.OnError; +import javax.websocket.OnMessage; +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; +import javax.websocket.server.ServerEndpointConfig; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.cerberus.core.crud.entity.TestCaseExecution; +import org.cerberus.core.websocket.decoders.QueueStatusDecoder; +import org.cerberus.core.websocket.encoders.QueueStatusEncoder; + +/** + * {@link ServerEndpoint} to be kept informed about {@link TestCaseExecution} + * changes + * + * @author corentin + * @author abourdon + */ +@ServerEndpoint( + value = "/queuestatus", + configurator = QueueStatusEndPoint.SingletonConfigurator.class, + decoders = {QueueStatusDecoder.class}, + encoders = {QueueStatusEncoder.class} +) +public class QueueStatusEndPoint { + + /** + * The {@link javax.websocket.server.ServerEndpointConfig.Configurator} of + * this {@link ServerEndpoint} that give always the same + * {@link TestCaseExecutionEndPoint} instance to deserve websocket support. + */ + public static class SingletonConfigurator extends ServerEndpointConfig.Configurator { + + @SuppressWarnings("unchecked") + @Override + public T getEndpointInstance(Class endpointClass) throws InstantiationException { + if (!QueueStatusEndPoint.class.equals(endpointClass)) { + throw new InstantiationException("No suitable instance for endpoint class " + endpointClass.getName()); + } + return (T) QueueStatusEndPoint.getInstance(); + } + + } + + /** + * The associated {@link Logger} to this class + */ + private static final Logger LOG = LogManager.getLogger(QueueStatusEndPoint.class); + + /** + * The unique instance of this {@link TestCaseExecutionEndPoint} class + */ + private static final QueueStatusEndPoint INSTANCE = new QueueStatusEndPoint(); + + /** + * Get the unique instance of this {@link TestCaseExecutionEndPoint} class + * + * @return the unique instance of this {@link TestCaseExecutionEndPoint} + */ + public static QueueStatusEndPoint getInstance() { + return INSTANCE; + } + + /** + * All open WebSocket sessions, grouped by executions + */ + private Lock mainLock = new ReentrantLock(); + private Map sessions = new HashMap<>(); +// private Map> executions = new HashMap<>(); + private Set queueStatuss; + + /** + * Send the given {@link TestCaseExecution} for all session opened to this + * execution. + *

+ * Message is sent only if the current timestamp is out of the + * {@link TestCaseExecution#getCerberus_featureflipping_websocketpushperiod()} + * + * @param queueStatus the {@link TestCaseExecution} to send to opened + * sessions + * @param forcePush if send has to be forced, regardless of the + * {@link TestCaseExecution#getCerberus_featureflipping_websocketpushperiod()}} + * @see TestCaseExecution#getLastWebsocketPush() + */ + public void send(QueueStatus queueStatus, boolean forcePush) { + // Check if sending is enabled +// if (!queueStatus.isCerberus_featureflipping_activatewebsocketpush()) { +// LOG.debug("Push is disabled. Ignore sending of execution " + queueStatus.getId()); +// return; +// } + + // Check if sending can be done regarding on the last push and allowed period +// long sinceLastPush = new Date().getTime() - queueStatus.getLastWebsocketPush(); +// if ((sinceLastPush < queueStatus.getCerberus_featureflipping_websocketpushperiod()) && !forcePush) { +// LOG.debug("Not enough elapsed time since the last push for execution " + queueStatus.getId() + " (" + sinceLastPush + " < " + queueStatus.getCerberus_featureflipping_websocketpushperiod()); +// return; +// } + // Get registered sessions + Collection registeredSessions = new ArrayList<>(); + mainLock.lock(); + try { + Set registeredSessionIds = queueStatuss; + if (registeredSessionIds != null) { + registeredSessions = Maps.filterKeys(sessions, Predicates.in(registeredSessionIds)).values(); + } + } finally { + mainLock.unlock(); + } + + // Send the given TestCaseExecution to all registered sessions + LOG.debug("Trying to send queue status to sessions"); + for (Session registeredSession : registeredSessions) { + try { + registeredSession.getBasicRemote().sendObject(queueStatus); + LOG.debug("Queue Status sent to session " + registeredSession.getId()); + } catch (Exception e) { + LOG.warn("Unable to send queue status to session " + registeredSession.getId() + " due to " + e.getMessage()); + } + } + + // Finally set the last push date to the given TestCaseExecution + queueStatus.setLastWebsocketPush(new Date().getTime()); + } + + /** + * Process to the end of the given {@link TestCaseExecution}, i.e., close + * all registered session to the given {@link TestCaseExecution} + * + * @param queueStatus the given {@link TestCaseExecution} to end + */ + public void end(QueueStatus queueStatus) { + // Get the registered sessions to the given TestCaseExecution + Collection registeredSessions = new ArrayList<>(); + mainLock.lock(); + try { + Set registeredSessionIds = queueStatuss; + if (registeredSessionIds != null) { + for (String registeredSessionId : registeredSessionIds) { + registeredSessions.add(sessions.remove(registeredSessionId)); + } + } + } finally { + mainLock.unlock(); + } + + // Close registered sessions + if (LOG.isDebugEnabled()) { + LOG.debug("Clean execution "); + } + for (Session registeredSession : registeredSessions) { + try { + registeredSession.close(); + } catch (Exception e) { + LOG.warn("Unable to close session " + registeredSession.getId() + " for queue status due to " + e.getMessage()); + } + } + } + + /** + * Callback when receiving message from client side + * + * @param session the client {@link Session} + * @param queueStatus + * @param executionId the execution identifier from the + * {@link ServerEndpoint} path + */ + @OnMessage + public void message(final Session session, QueueStatus queueStatus) { + // Nothing to do + } + + /** + * Callback when receiving opened connection from client side + * + * @param session the client {@link Session} + * @param config the associated {@link EndpointConfig} to the new connection + * @param executionId the execution identifier from the + * {@link ServerEndpoint} path + */ + @OnOpen + public void openConnection(Session session, EndpointConfig config) { + LOG.debug("Session " + session.getId() + " opened connection to queue Status"); + mainLock.lock(); + try { + sessions.put(session.getId(), session); + Set registeredSessions = queueStatuss; + if (registeredSessions == null) { + registeredSessions = new HashSet<>(); + } + registeredSessions.add(session.getId()); + queueStatuss = registeredSessions; + } finally { + mainLock.unlock(); + } + } + + /** + * Callback when receiving closed connection from client side + * + * @param session the client {@link Session} + * @param executionId the execution identifier from the + * {@link ServerEndpoint} path + */ + @OnClose + public void closedConnection(Session session, @PathParam("execution-id") long executionId) { + if (LOG.isDebugEnabled()) { + LOG.debug("Session " + session.getId() + " closed connection to execution " + executionId); + } + mainLock.lock(); + try { + sessions.remove(session.getId()); + Set registeredSessions = queueStatuss; + if (registeredSessions != null) { + registeredSessions.remove(session.getId()); + } + } finally { + mainLock.unlock(); + } + } + + /** + * Callback when receiving error connection from client side + * + * @param session the client {@link Session} + * @param error the associated {@link Throwable} to the received error + */ + @OnError + public void error(Session session, Throwable error) { + LOG.warn("An error occurred during websocket communication with session " + session.getId() + ": " + error.getMessage(), error); + try { + session.getBasicRemote().sendText(error.getMessage()); + } catch (Exception e) { + LOG.warn("Unable to send error to session " + session.getId() + " due to " + e.getMessage()); + } + } + +} diff --git a/source/src/main/java/org/cerberus/core/websocket/TestCaseExecutionEndPoint.java b/source/src/main/java/org/cerberus/core/websocket/TestCaseExecutionEndPoint.java index b945e08868..633592a9d9 100644 --- a/source/src/main/java/org/cerberus/core/websocket/TestCaseExecutionEndPoint.java +++ b/source/src/main/java/org/cerberus/core/websocket/TestCaseExecutionEndPoint.java @@ -119,18 +119,14 @@ public static TestCaseExecutionEndPoint getInstance() { public void send(TestCaseExecution execution, boolean forcePush) { // Check if sending is enabled if (!execution.isCerberus_featureflipping_activatewebsocketpush()) { - if (LOG.isDebugEnabled()) { - LOG.debug("Push is disabled. Ignore sending of execution " + execution.getId()); - } + LOG.debug("Push is disabled. Ignore sending of execution " + execution.getId()); return; } // Check if sending can be done regarding on the last push and allowed period long sinceLastPush = new Date().getTime() - execution.getLastWebsocketPush(); if ((sinceLastPush < execution.getCerberus_featureflipping_websocketpushperiod()) && !forcePush) { - if (LOG.isDebugEnabled()) { - LOG.debug("Not enough elapsed time since the last push for execution " + execution.getId() + " (" + sinceLastPush + " < " + execution.getCerberus_featureflipping_websocketpushperiod()); - } + LOG.debug("Not enough elapsed time since the last push for execution " + execution.getId() + " (" + sinceLastPush + " < " + execution.getCerberus_featureflipping_websocketpushperiod()); return; } @@ -147,15 +143,11 @@ public void send(TestCaseExecution execution, boolean forcePush) { } // Send the given TestCaseExecution to all registered sessions - if (LOG.isDebugEnabled()) { - LOG.debug("Trying to send execution " + execution.getId() + " to sessions"); - } + LOG.debug("Trying to send execution " + execution.getId() + " to sessions"); for (Session registeredSession : registeredSessions) { try { registeredSession.getBasicRemote().sendObject(execution); - if (LOG.isDebugEnabled()) { - LOG.debug("Execution " + execution.getId() + " sent to session " + registeredSession.getId()); - } + LOG.debug("Execution " + execution.getId() + " sent to session " + registeredSession.getId()); } catch (Exception e) { LOG.warn("Unable to send execution " + execution.getId() + " to session " + registeredSession.getId() + " due to " + e.getMessage()); } diff --git a/source/src/main/java/org/cerberus/core/websocket/decoders/QueueStatusDecoder.java b/source/src/main/java/org/cerberus/core/websocket/decoders/QueueStatusDecoder.java new file mode 100644 index 0000000000..235305e739 --- /dev/null +++ b/source/src/main/java/org/cerberus/core/websocket/decoders/QueueStatusDecoder.java @@ -0,0 +1,54 @@ +/** + * Cerberus Copyright (C) 2013 - 2017 cerberustesting + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file is part of Cerberus. + * + * Cerberus is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Cerberus is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Cerberus. If not, see . + */ +package org.cerberus.core.websocket.decoders; + +import com.google.gson.Gson; + +import javax.websocket.DecodeException; +import javax.websocket.Decoder; +import javax.websocket.EndpointConfig; +import org.cerberus.core.websocket.QueueStatus; + +/** + * Created by corentin on 31/10/16. + */ +public class QueueStatusDecoder implements Decoder.Text { + + @Override + public QueueStatus decode(String s) throws DecodeException { + Gson gson = new Gson(); + return gson.fromJson(s, QueueStatus.class); + } + + @Override + public boolean willDecode(String s) { + return false; + } + + @Override + public void init(EndpointConfig endpointConfig) { + + } + + @Override + public void destroy() { + + } +} diff --git a/source/src/main/java/org/cerberus/core/websocket/decoders/TestCaseExecutionDecoder.java b/source/src/main/java/org/cerberus/core/websocket/decoders/TestCaseExecutionDecoder.java index 46cdc06f22..82476c400a 100644 --- a/source/src/main/java/org/cerberus/core/websocket/decoders/TestCaseExecutionDecoder.java +++ b/source/src/main/java/org/cerberus/core/websocket/decoders/TestCaseExecutionDecoder.java @@ -34,7 +34,7 @@ public class TestCaseExecutionDecoder implements Decoder.Text @Override public TestCaseExecution decode(String s) throws DecodeException { Gson gson = new Gson(); - return gson.fromJson(s,TestCaseExecution.class); + return gson.fromJson(s, TestCaseExecution.class); } @Override diff --git a/source/src/main/java/org/cerberus/core/websocket/encoders/QueueStatusEncoder.java b/source/src/main/java/org/cerberus/core/websocket/encoders/QueueStatusEncoder.java new file mode 100644 index 0000000000..6b555a4aac --- /dev/null +++ b/source/src/main/java/org/cerberus/core/websocket/encoders/QueueStatusEncoder.java @@ -0,0 +1,52 @@ +/** + * Cerberus Copyright (C) 2013 - 2017 cerberustesting + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file is part of Cerberus. + * + * Cerberus is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Cerberus is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Cerberus. If not, see . + */ +package org.cerberus.core.websocket.encoders; + +import org.cerberus.core.crud.service.ITestCaseStepExecutionService; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.websocket.EncodeException; +import javax.websocket.Encoder; +import javax.websocket.EndpointConfig; +import org.cerberus.core.websocket.QueueStatus; + +/** + * Created by vertigo on 16/06/24. + */ +public class QueueStatusEncoder implements Encoder.Text { + + @Autowired + ITestCaseStepExecutionService testCaseStepExecutionService; + + @Override + public String encode(QueueStatus queueStatus) throws EncodeException { + return queueStatus.toJson(true).toString(); + } + + @Override + public void init(EndpointConfig endpointConfig) { + + } + + @Override + public void destroy() { + + } +} diff --git a/source/src/main/java/org/cerberus/core/websocket/encoders/TestCaseExecutionEncoder.java b/source/src/main/java/org/cerberus/core/websocket/encoders/TestCaseExecutionEncoder.java index bb108b802f..3b7fe0bbf8 100644 --- a/source/src/main/java/org/cerberus/core/websocket/encoders/TestCaseExecutionEncoder.java +++ b/source/src/main/java/org/cerberus/core/websocket/encoders/TestCaseExecutionEncoder.java @@ -30,14 +30,14 @@ /** * Created by corentin on 31/10/16. */ -public class TestCaseExecutionEncoder implements Encoder.Text { +public class TestCaseExecutionEncoder implements Encoder.Text { @Autowired ITestCaseStepExecutionService testCaseStepExecutionService; @Override public String encode(TestCaseExecution testCaseExecution) throws EncodeException { - return testCaseExecution.toJson(true).toString(); + return testCaseExecution.toJson(true).toString(); } @Override diff --git a/source/src/main/webapp/Homepage.jsp b/source/src/main/webapp/Homepage.jsp index 2d88aab054..dade4a7209 100644 --- a/source/src/main/webapp/Homepage.jsp +++ b/source/src/main/webapp/Homepage.jsp @@ -106,17 +106,17 @@ -

+
-
+
Test Execution
Launch Test Case
- @@ -132,7 +142,7 @@
-