Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added STOMP messaging between the front and backends [#615] #641

Merged
merged 1 commit into from Mar 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 0 additions & 1 deletion comixed-app/src/main/resources/application.properties
Expand Up @@ -40,4 +40,3 @@ spring.jackson.deserialization.fail-on-unknown-properties=false
# Logging
logging.level.root=INFO
logging.level.org.comixedproject=DEBUG

@@ -0,0 +1,36 @@
/*
* ComiXed - A digital comic book library management application.
* Copyright (C) 2020, The ComiXed Project
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses>
*/

package org.comixedproject.model.state.messaging;

import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Getter;

/**
* <code>TaskCountMessage</code> is the message that's send to the task count queue.
*
* @author Darryl L. Pierce
*/
@AllArgsConstructor
public class TaskCountMessage implements Serializable {
@JsonProperty("count")
@Getter
private long taskCount;
}
4 changes: 4 additions & 0 deletions comixed-rest-api/pom.xml
Expand Up @@ -22,6 +22,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
Expand Down
@@ -0,0 +1,50 @@
/*
* ComiXed - A digital comic book library management application.
* Copyright (C) 2021, The ComiXed Project
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses>
*/

package org.comixedproject.state;

import lombok.extern.log4j.Log4j2;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

/**
* <code>ComiXedWebSocketConfig</code> provides the configuration for using websockets.
*
* @author Darryl L. Pierce
*/
@Configuration
@EnableWebSocketMessageBroker
@Log4j2
public class ComiXedWebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(final MessageBrokerRegistry registry) {
log.trace("Configuring websocket message broker");
registry.enableSimpleBroker("/topic");
registry.setApplicationDestinationPrefixes("/comixed");
registry.setUserDestinationPrefix("/secured/user");
}

@Override
public void registerStompEndpoints(final StompEndpointRegistry registry) {
log.trace("Configuration STOMP endpoints");
registry.addEndpoint("/ws").setAllowedOrigins("*").withSockJS();
}
}
Expand Up @@ -157,4 +157,14 @@ public void clearTaskAuditLog() {
public Map<Long, TaskType> getRunningTasks() {
return this.runningTasks;
}

/**
* Return the number of tasks both in the database and running.
*
* @return the task count
*/
public long getTaskCount() {
log.debug("Fetching the number of tasks in the database");
return this.taskRepository.count() + this.runningTasks.size();
}
}
@@ -0,0 +1,38 @@
/*
* ComiXed - A digital comic book library management application.
* Copyright (C) 2021, The ComiXed Project
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses>
*/

package org.comixedproject.task;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

/**
* <code>TaskContext</code> provides a runtime context for the task runner environment.
*
* @author Darryl L. Pierce
*/
@Configuration
public class TaskContext {
@Bean
@Qualifier("CxTaskExecutor")
public ThreadPoolTaskExecutor getTaskExecutor() {
return new ThreadPoolTaskExecutor();
}
}
Expand Up @@ -48,11 +48,10 @@
@ConfigurationProperties(prefix = "task", ignoreUnknownFields = false)
@Log4j2
public class WorkerTaskAdaptor implements InitializingBean {
Map<TaskType, String> adaptorMap = new EnumMap<>(TaskType.class);
@Autowired private ApplicationContext applicationContext;
@Autowired private TaskService taskService;

private List<TaskTypeEntry> adaptors = new ArrayList<>();
Map<TaskType, String> adaptorMap = new EnumMap<>(TaskType.class);

public List<TaskTypeEntry> getAdaptors() {
return adaptors;
Expand Down Expand Up @@ -101,6 +100,16 @@ public <T extends WorkerTaskEncoder> T getEncoder(final TaskType taskType) throw
return (T) this.applicationContext.getBean(this.adaptorMap.get(taskType));
}

/**
* Returns the total number of tasks running and in the database.
*
* @return the task count
*/
public long getTaskCount() {
log.debug("Getting the task count");
return this.taskService.getTaskCount();
}

public static class TaskTypeEntry {
private TaskType type;
private String name;
Expand Down
Expand Up @@ -20,13 +20,15 @@

import java.util.List;
import lombok.extern.log4j.Log4j2;
import org.comixedproject.model.state.messaging.TaskCountMessage;
import org.comixedproject.model.tasks.Task;
import org.comixedproject.task.TaskException;
import org.comixedproject.task.adaptors.WorkerTaskAdaptor;
import org.comixedproject.task.encoders.WorkerTaskEncoder;
import org.comixedproject.task.runner.TaskManager;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Component;

/**
Expand All @@ -39,8 +41,11 @@
@Component
@Log4j2
public class MonitorTaskQueueWorkerTask extends AbstractWorkerTask implements InitializingBean {
public static final String TASK_UPDATE_TARGET = "/topic/taskcount";

@Autowired private TaskManager taskManager;
@Autowired private WorkerTaskAdaptor workerTaskAdaptor;
@Autowired private SimpMessagingTemplate messagingTemplate;

@Override
protected String createDescription() {
Expand All @@ -55,6 +60,9 @@ public void afterPropertiesSet() throws Exception {

@Override
public void startTask() throws WorkerTaskException {
log.debug("Updating topic: task count");
this.messagingTemplate.convertAndSend(
TASK_UPDATE_TARGET, new TaskCountMessage(this.workerTaskAdaptor.getTaskCount()));
log.debug("Checking queue for waiting tasks");
final List<Task> taskQueue = this.workerTaskAdaptor.getNextTask();
for (int index = 0; index < taskQueue.size(); index++) {
Expand Down
Expand Up @@ -29,6 +29,7 @@
import org.comixedproject.task.model.WorkerTask;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

Expand All @@ -40,7 +41,10 @@
@Component
@Log4j2
public class TaskManager implements InitializingBean {
@Autowired private ThreadPoolTaskExecutor taskExecutor;
@Autowired
@Qualifier("CxTaskExecutor")
private ThreadPoolTaskExecutor taskExecutor;

@Autowired private TaskService taskService;

/**
Expand Down
Expand Up @@ -19,9 +19,11 @@
package org.comixedproject.task.model;

import static junit.framework.TestCase.assertNotNull;
import static org.comixedproject.task.model.MonitorTaskQueueWorkerTask.TASK_UPDATE_TARGET;

import java.util.ArrayList;
import java.util.List;
import org.comixedproject.model.state.messaging.TaskCountMessage;
import org.comixedproject.model.tasks.Task;
import org.comixedproject.model.tasks.TaskType;
import org.comixedproject.task.TaskException;
Expand All @@ -30,10 +32,9 @@
import org.comixedproject.task.runner.TaskManager;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.*;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.messaging.simp.SimpMessagingTemplate;

@RunWith(MockitoJUnitRunner.class)
public class MonitorTaskQueueWorkerTaskTest {
Expand All @@ -45,6 +46,9 @@ public class MonitorTaskQueueWorkerTaskTest {
@Mock private WorkerTaskEncoder<?> workerTaskEncoder;
@Mock private Task task;
@Mock private WorkerTask workerTask;
@Mock private SimpMessagingTemplate messagingTemplate;

@Captor private ArgumentCaptor<TaskCountMessage> taskCountMessageArgumentCaptor;

private List<Task> taskList = new ArrayList<>();

Expand Down Expand Up @@ -96,6 +100,9 @@ public void testStartTask() throws WorkerTaskException, TaskException {
Mockito.when(workerTaskAdaptor.getEncoder(Mockito.any(TaskType.class)))
.thenReturn(workerTaskEncoder);
Mockito.when(workerTaskEncoder.decode(Mockito.any(Task.class))).thenReturn(workerTask);
Mockito.doNothing()
.when(messagingTemplate)
.convertAndSend(Mockito.anyString(), taskCountMessageArgumentCaptor.capture());

monitorTaskQueueWorkerTask.startTask();

Expand All @@ -104,6 +111,8 @@ public void testStartTask() throws WorkerTaskException, TaskException {
Mockito.verify(workerTaskAdaptor, Mockito.times(taskList.size())).getEncoder(TEST_TASK_TYPE);
Mockito.verify(workerTaskEncoder, Mockito.times(taskList.size())).decode(task);
Mockito.verify(taskManager, Mockito.times(taskList.size())).runTask(workerTask, task);
Mockito.verify(messagingTemplate, Mockito.times(1))
.convertAndSend(TASK_UPDATE_TARGET, taskCountMessageArgumentCaptor.getValue());
}

@Test
Expand Down
2 changes: 1 addition & 1 deletion comixed-web/angular.json
Expand Up @@ -29,7 +29,7 @@
"./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css",
"src/styles.scss"
],
"scripts": []
"scripts": ["./node_modules/sockjs-client/dist/sockjs.min.js"]
},
"configurations": {
"production": {
Expand Down
5 changes: 3 additions & 2 deletions comixed-web/package.json
Expand Up @@ -11,11 +11,10 @@
"code-cleanup": "prettier --write 'src/app/**/*.{ts,json,html,scss}'"
},
"prettier": {
"singleQuote": true,
"singleQuote": true,
"useTabs": false,
"bracketSpacing": true,
"trailingComma": "none",
"bracketSpacing": true,
"jsxBracketSameLine": true,
"arrowParens": "avoid",
"overrides": [
Expand Down Expand Up @@ -61,9 +60,11 @@
"object-path": "^0.11.5",
"rxjs": "~6.5.5",
"socket.io": "^2.4",
"sockjs-client": "^1.5.0",
"tslib": "^2.0.0",
"webpack": "5.4.0",
"webpack-subresource-integrity": "^1.5.1",
"webstomp-client": "^1.2.6",
"zone.js": "~0.10.3"
},
"devDependencies": {
Expand Down
11 changes: 11 additions & 0 deletions comixed-web/proxy.conf.json
Expand Up @@ -9,5 +9,16 @@
"secure": false,
"changeOrigin": true,
"logLevel": "debug"
},
"/ws": {
"target": {
"host": "localhost",
"protocol": "http",
"port": 7171
},
"auth": "user:password",
"secure": false,
"changeOrigin": true,
"logLevel": "debug"
}
}