├── dashboard ├── .nvmrc ├── README.md ├── public │ └── favicon.ico ├── .prettierignore ├── commons │ ├── constants.js │ └── validator.js ├── .prettierrc ├── next.config.cjs ├── components │ ├── menu │ │ └── presenter │ │ │ ├── styles.js │ │ │ └── index.js │ ├── queue-table │ │ ├── presenter │ │ │ ├── styles.js │ │ │ └── index.js │ │ └── container │ │ │ └── index.js │ └── command-dialog │ │ └── presenter │ │ └── index.js ├── pages │ ├── index.js │ ├── _app.js │ └── _document.js ├── api-service │ ├── api.js │ ├── query-queue-service.js │ └── command-queue-service.js ├── .gitignore ├── styles │ ├── default-theme.js │ ├── mui-theme.js │ └── global-theme.js ├── package.json └── test │ └── commons │ └── validator.spec.js ├── jitpack.yml ├── integration-tests ├── READMD.md ├── stress │ ├── src │ │ └── test │ │ │ ├── resources │ │ │ └── application.yml │ │ │ └── java │ │ │ └── com │ │ │ └── github │ │ │ └── dhslrl321 │ │ │ └── zsmq │ │ │ └── rpc │ │ │ ├── ResultReporter.java │ │ │ ├── RpcCallDequeueTest.java │ │ │ └── RpcCallEnqueueTest.java │ ├── README.md │ └── build.gradle ├── polling-queue-prod-cons │ ├── src │ │ ├── test │ │ │ ├── resources │ │ │ │ └── application.yml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── github │ │ │ │ └── dhslrl321 │ │ │ │ └── zsmq │ │ │ │ └── integration │ │ │ │ ├── FooZolaMessageListener.java │ │ │ │ ├── BeforeTestConfiguration.java │ │ │ │ └── SimpleQueueIntegrationTest.java │ │ └── main │ │ │ └── java │ │ │ └── com │ │ │ └── github │ │ │ └── dhslrl321 │ │ │ └── zsmq │ │ │ └── Main.java │ └── build.gradle └── spring-boot-starter-integrations │ ├── src │ ├── test │ │ ├── resources │ │ │ ├── application-consuming.yml │ │ │ └── application-producing.yml │ │ └── java │ │ │ └── com │ │ │ └── github │ │ │ └── dhslrl321 │ │ │ └── zsmq │ │ │ └── integration │ │ │ ├── ProducingModeTests.java │ │ │ └── BeanAutoCreationTests.java │ └── main │ │ └── java │ │ └── com │ │ └── github │ │ └── dhslrl321 │ │ └── zsmq │ │ └── Main.java │ └── build.gradle ├── zola-messaging-server ├── src │ ├── main │ │ ├── resources │ │ │ └── application.yml │ │ └── java │ │ │ └── com │ │ │ └── github │ │ │ └── dhslrl321 │ │ │ ├── zsmq │ │ │ ├── controller │ │ │ │ ├── model │ │ │ │ │ ├── SimpleResponse.java │ │ │ │ │ ├── PayloadModel.java │ │ │ │ │ ├── QueueNameModel.java │ │ │ │ │ ├── MessageModel.java │ │ │ │ │ ├── HeaderModel.java │ │ │ │ │ └── ModelConverter.java │ │ │ │ ├── handler │ │ │ │ │ └── GlobalExceptionHandler.java │ │ │ │ ├── QueryQueueController.java │ │ │ │ └── CommandQueueController.java │ │ │ └── config │ │ │ │ └── ZolaBeanConfig.java │ │ │ └── ServerApplication.java │ └── test │ │ └── java │ │ └── com │ │ └── github │ │ └── dhslrl321 │ │ └── zsmq │ │ └── BeanRegisterTest.java ├── README.md └── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── zola-messaging-core ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── github │ │ │ └── dhslrl321 │ │ │ └── zsmq │ │ │ ├── listener │ │ │ ├── DeletionPolicy.java │ │ │ ├── MessageListener.java │ │ │ ├── strategy │ │ │ │ ├── ZolaMessagePollingException.java │ │ │ │ └── ListeningStrategy.java │ │ │ ├── task │ │ │ │ ├── ListeningTaskExecutor.java │ │ │ │ ├── ListeningTaskFactory.java │ │ │ │ ├── AbstractListeningTaskFactory.java │ │ │ │ └── ListeningTask.java │ │ │ ├── InvalidUseOfZolaMessageListenerException.java │ │ │ ├── ListeningInformation.java │ │ │ └── ZolaMessageListeningProcessor.java │ │ │ ├── core │ │ │ ├── message │ │ │ │ ├── MediaTypes.java │ │ │ │ ├── ZolaPayload.java │ │ │ │ ├── ZolaHeader.java │ │ │ │ └── ZolaMessage.java │ │ │ └── queue │ │ │ │ ├── QueueName.java │ │ │ │ ├── Describable.java │ │ │ │ ├── QueueDescribe.java │ │ │ │ ├── QueueInfo.java │ │ │ │ ├── ZolaQueue.java │ │ │ │ ├── AbstractZolaQueue.java │ │ │ │ ├── ZolaSimpleQueue.java │ │ │ │ └── ZolaQueueContainer.java │ │ │ ├── converter │ │ │ ├── Registrable.java │ │ │ ├── Supportable.java │ │ │ ├── MessageConverter.java │ │ │ ├── MessageConverters.java │ │ │ ├── StringMessageConverter.java │ │ │ ├── CompositeMessageConverter.java │ │ │ └── JsonMessageConverter.java │ │ │ ├── exception │ │ │ ├── QueueNotFoundException.java │ │ │ ├── EmptyQueueException.java │ │ │ └── ZolaSerializeException.java │ │ │ ├── commons │ │ │ ├── Pair.java │ │ │ └── ZolaJsonSerializer.java │ │ │ ├── http │ │ │ ├── ZolaServerCommunicationException.java │ │ │ ├── ZolaServerConnectionFailedException.java │ │ │ └── ZolaHttpClient.java │ │ │ ├── client │ │ │ ├── ZolaClientConfig.java │ │ │ └── ZolaQueueMessageTemplate.java │ │ │ └── detector │ │ │ └── MessageListenerDetector.java │ └── test │ │ └── java │ │ └── com │ │ └── github │ │ └── dhslrl321 │ │ └── zsmq │ │ ├── TODO.md │ │ ├── listener │ │ ├── task │ │ │ └── ListeningTaskTest.java │ │ └── ZolaMessageListeningProcessorTest.java │ │ ├── core │ │ └── queue │ │ │ ├── AbstractZolaQueueTest.java │ │ │ ├── ZolaSimpleQueueTest.java │ │ │ └── ZolaQueueContainerTest.java │ │ ├── client │ │ ├── spy │ │ │ └── SpyZolaHttpClient.java │ │ └── ZolaQueueMessageTemplateTest.java │ │ ├── commons │ │ └── ZolaJsonSerializerTest.java │ │ ├── converter │ │ ├── StringMessageConverterTest.java │ │ ├── JsonMessageConverterTest.java │ │ └── CompositeMessageConverterTest.java │ │ └── SharedFixture.java ├── README.md └── build.gradle ├── zola-messaging-spring-boot-autoconfigure ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── spring.factories │ │ └── java │ │ └── com │ │ └── github │ │ └── dhslrl321 │ │ └── zsmq │ │ ├── ZsmqProperty.java │ │ └── ZsmqAutoconfiguration.java └── build.gradle ├── .githook └── pre-push ├── zola-messaging-spring-boot-starter └── build.gradle ├── zola-messaging-spring-sdk ├── README.md ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── github │ │ │ └── dhslrl321 │ │ │ └── zsmq │ │ │ ├── annotation │ │ │ ├── ZolaConsumer.java │ │ │ └── ZolaMessageListener.java │ │ │ ├── listener │ │ │ ├── SpringBeanMessageListener.java │ │ │ ├── task │ │ │ │ ├── ThreadPoolListeningExecutor.java │ │ │ │ └── PollingQueueListeningTaskFactory.java │ │ │ └── strategy │ │ │ │ └── HttpPollingListeningStrategy.java │ │ │ └── detector │ │ │ └── SpringBeanMessageListenerDetector.java │ └── test │ │ └── java │ │ └── com │ │ └── github │ │ └── dhslrl321 │ │ └── zsmq │ │ ├── listener │ │ ├── strategy │ │ │ ├── fake │ │ │ │ └── FakeHttpClient.java │ │ │ └── HttpPollingListeningStrategyTest.java │ │ └── task │ │ │ └── PollingQueueListeningTaskFactoryTest.java │ │ ├── SharedFixture.java │ │ └── detector │ │ └── SpringBeanMessageListenerDetectorTest.java └── build.gradle ├── settings.gradle ├── .gitignore ├── Makefile ├── README-kor.md ├── README.md ├── wiki ├── Getting Started-kor.md └── Getting Started-eng.md ├── gradlew └── LICENSE /dashboard/.nvmrc: -------------------------------------------------------------------------------- 1 | 16.17.0 2 | -------------------------------------------------------------------------------- /jitpack.yml: -------------------------------------------------------------------------------- 1 | jdk: 2 | - openjdk11 -------------------------------------------------------------------------------- /integration-tests/READMD.md: -------------------------------------------------------------------------------- 1 | # Testing 2 | 3 | - big-bang 4 | - stress -------------------------------------------------------------------------------- /zola-messaging-server/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8291 -------------------------------------------------------------------------------- /dashboard/README.md: -------------------------------------------------------------------------------- 1 | This is a starter template for [Learn Next.js](https://nextjs.org/learn). -------------------------------------------------------------------------------- /zola-messaging-server/README.md: -------------------------------------------------------------------------------- 1 | # ZSMQ's messaging server 2 | 3 | this is ZSMQ's messaging server -------------------------------------------------------------------------------- /dashboard/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dhslrl321/zsmq/HEAD/dashboard/public/favicon.ico -------------------------------------------------------------------------------- /dashboard/.prettierignore: -------------------------------------------------------------------------------- 1 | .next 2 | next-env.d.ts 3 | node_modules 4 | yarn.lock 5 | package-lock.json 6 | public -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dhslrl321/zsmq/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /integration-tests/stress/src/test/resources/application.yml: -------------------------------------------------------------------------------- 1 | zsmq: 2 | url: http://localhost:8291 3 | listening: false -------------------------------------------------------------------------------- /dashboard/commons/constants.js: -------------------------------------------------------------------------------- 1 | export const ERROR_BAD_REQUEST = 'ERROR_BAD_REQUEST'; 2 | export const ERROR_NOT_FOUND = 'ERROR_NOT_FOUND'; -------------------------------------------------------------------------------- /integration-tests/polling-queue-prod-cons/src/test/resources/application.yml: -------------------------------------------------------------------------------- 1 | zsmq: 2 | url: http://localhost:8291 3 | listening: true -------------------------------------------------------------------------------- /dashboard/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "semi": true, 4 | "singleQuote": true, 5 | "tabWidth": 2, 6 | "trailingComma": "es5" 7 | } -------------------------------------------------------------------------------- /integration-tests/spring-boot-starter-integrations/src/test/resources/application-consuming.yml: -------------------------------------------------------------------------------- 1 | zsmq: 2 | url: http://localhost:1234 3 | listening: false -------------------------------------------------------------------------------- /integration-tests/spring-boot-starter-integrations/src/test/resources/application-producing.yml: -------------------------------------------------------------------------------- 1 | zsmq: 2 | url: http://localhost:1234 3 | listening: true -------------------------------------------------------------------------------- /dashboard/commons/validator.js: -------------------------------------------------------------------------------- 1 | export const regexNumberAndEnglishAndHyphen = (provided) => { 2 | const regex = /^[A-Za-z0-9/g\\d_-]+$/; 3 | return regex.test(provided); 4 | }; 5 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/listener/DeletionPolicy.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.listener; 2 | 3 | public enum DeletionPolicy { 4 | ALWAYS 5 | } 6 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/core/message/MediaTypes.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.core.message; 2 | 3 | public enum MediaTypes { 4 | TEXT, JSON 5 | } 6 | -------------------------------------------------------------------------------- /zola-messaging-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | com.github.dhslrl321.zsmq.ZsmqAutoconfiguration -------------------------------------------------------------------------------- /dashboard/next.config.cjs: -------------------------------------------------------------------------------- 1 | // next.config.cjs 2 | module.exports = { 3 | // Prefer loading of ES Modules over CommonJS 4 | reactStrictMode: false, 5 | experimental: { esmExternals: true }, 6 | }; 7 | -------------------------------------------------------------------------------- /dashboard/components/menu/presenter/styles.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const RootDiv = styled.div` 4 | height: 100%; 5 | background: #3a444e; 6 | color: #ffffff; 7 | `; 8 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/converter/Registrable.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.converter; 2 | 3 | public interface Registrable { 4 | void register(T t); 5 | } 6 | -------------------------------------------------------------------------------- /zola-messaging-core/src/test/java/com/github/dhslrl321/zsmq/TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | 4 | # ING 5 | 6 | - ListeningTask 에 테스트 코드를 추가한다 7 | 8 | # DONE 9 | 10 | - ListeningTask 에 pair 자체를 넘기도록 한다. 11 | -------------------------------------------------------------------------------- /.githook/pre-push: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ## TODO delete push 에 대해서는 skip 3 | ## TODO 선택적 skip 기능 4 | 5 | if ! make test 2>&1 ; then 6 | echo "Error while testing the code" 7 | exit 1 8 | fi 9 | 10 | exit 0 11 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/exception/QueueNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.exception; 2 | 3 | public class QueueNotFoundException extends RuntimeException { 4 | } 5 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/listener/MessageListener.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.listener; 2 | 3 | public interface MessageListener { 4 | void listen(String message); 5 | } 6 | -------------------------------------------------------------------------------- /dashboard/pages/index.js: -------------------------------------------------------------------------------- 1 | import TableContainer from "../components/queue-table/container"; 2 | 3 | const Home = () => { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } 10 | export default Home; -------------------------------------------------------------------------------- /dashboard/api-service/api.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | const API = axios.create({ 4 | baseURL: 'http://localhost:8291', 5 | headers: { 6 | 'Content-Type': 'application/json' 7 | } 8 | }); 9 | 10 | export default API; -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/listener/strategy/ZolaMessagePollingException.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.listener.strategy; 2 | 3 | public class ZolaMessagePollingException extends RuntimeException { 4 | } 5 | -------------------------------------------------------------------------------- /zola-messaging-core/src/test/java/com/github/dhslrl321/zsmq/listener/task/ListeningTaskTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.listener.task; 2 | 3 | import static org.junit.jupiter.api.Assertions.*; 4 | 5 | class ListeningTaskTest { 6 | 7 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/core/queue/QueueName.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.core.queue; 2 | 3 | import lombok.Value; 4 | 5 | @Value(staticConstructor = "of") 6 | public class QueueName { 7 | String value; 8 | } 9 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/core/message/ZolaPayload.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.core.message; 2 | 3 | import lombok.Value; 4 | 5 | @Value(staticConstructor = "of") 6 | public class ZolaPayload { 7 | String value; 8 | } 9 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/commons/Pair.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.commons; 2 | 3 | import lombok.Value; 4 | 5 | @Value(staticConstructor = "of") 6 | public class Pair { 7 | LEFT left; 8 | RIGHT right; 9 | } 10 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/listener/task/ListeningTaskExecutor.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.listener.task; 2 | 3 | import java.util.List; 4 | 5 | public interface ListeningTaskExecutor { 6 | void executeAll(List tasks); 7 | } 8 | -------------------------------------------------------------------------------- /integration-tests/stress/README.md: -------------------------------------------------------------------------------- 1 | # stress-tests 2 | 3 | - RPC call 4 | - 1,000,000 개의 메시지를 enqueue 하는데 걸리는 시간 5 | - 차수 6 | 1. 158947 (ms), 158 (s), 초당 6,329 개 7 | 2. 158792 (ms), 158 (s), 초당 6,329 개 8 | 3. 152741 (ms), 152 (s), 초당 6,578 개 9 | - 1,000,000 개의 메시지를 dequeue 하는데 걸리는 시간 -------------------------------------------------------------------------------- /dashboard/api-service/query-queue-service.js: -------------------------------------------------------------------------------- 1 | import API from "./api"; 2 | 3 | export const getQueueDescribeAPI = async () => { 4 | try { 5 | const { data } = await API.get('/api/queues'); 6 | return data; 7 | } catch { 8 | console.log('error when request zsmq server') 9 | } 10 | } -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/exception/EmptyQueueException.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.exception; 2 | 3 | public class EmptyQueueException extends RuntimeException { 4 | public EmptyQueueException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/exception/ZolaSerializeException.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.exception; 2 | 3 | public class ZolaSerializeException extends RuntimeException { 4 | public ZolaSerializeException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/core/queue/Describable.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.core.queue; 2 | 3 | import java.time.LocalDateTime; 4 | 5 | public interface Describable { 6 | QueueName getName(); 7 | 8 | LocalDateTime getCreatedAt(); 9 | 10 | int size(); 11 | } 12 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/http/ZolaServerCommunicationException.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.http; 2 | 3 | public class ZolaServerCommunicationException extends RuntimeException { 4 | public ZolaServerCommunicationException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /zola-messaging-server/src/main/java/com/github/dhslrl321/zsmq/controller/model/SimpleResponse.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.controller.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | @Data 7 | @AllArgsConstructor 8 | public class SimpleResponse { 9 | private String message; 10 | } 11 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/client/ZolaClientConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.client; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | @AllArgsConstructor 7 | public class ZolaClientConfig { 8 | @Getter 9 | private final String serverBaseUrl; 10 | } 11 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/core/queue/QueueDescribe.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.core.queue; 2 | 3 | import java.util.List; 4 | import lombok.Value; 5 | 6 | @Value(staticConstructor = "of") 7 | public class QueueDescribe { 8 | int totalQueueSize; 9 | List detail; 10 | } 11 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/http/ZolaServerConnectionFailedException.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.http; 2 | 3 | public class ZolaServerConnectionFailedException extends RuntimeException { 4 | public ZolaServerConnectionFailedException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/converter/Supportable.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.converter; 2 | 3 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 4 | 5 | public interface Supportable { 6 | 7 | boolean isSupport(Object payload); 8 | 9 | boolean isSupport(ZolaMessage message); 10 | } 11 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/core/queue/QueueInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.core.queue; 2 | 3 | import java.time.LocalDateTime; 4 | import lombok.Value; 5 | 6 | @Value(staticConstructor = "of") 7 | public class QueueInfo { 8 | String name; 9 | int messagesAvailable; 10 | LocalDateTime created; 11 | } 12 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/listener/InvalidUseOfZolaMessageListenerException.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.listener; 2 | 3 | public class InvalidUseOfZolaMessageListenerException extends RuntimeException { 4 | public InvalidUseOfZolaMessageListenerException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/core/queue/ZolaQueue.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.core.queue; 2 | 3 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 4 | 5 | public interface ZolaQueue extends Describable { 6 | ZolaMessage peek(); 7 | 8 | ZolaMessage pop(); 9 | 10 | void push(ZolaMessage zolaMessage); 11 | } 12 | -------------------------------------------------------------------------------- /zola-messaging-spring-boot-starter/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | } 4 | 5 | repositories { 6 | mavenCentral() 7 | } 8 | 9 | dependencies { 10 | api project(':zola-messaging-core') 11 | api project(':zola-messaging-spring-sdk') 12 | api project(':zola-messaging-spring-boot-autoconfigure') 13 | } 14 | 15 | test { 16 | useJUnitPlatform() 17 | } -------------------------------------------------------------------------------- /zola-messaging-server/src/main/java/com/github/dhslrl321/zsmq/controller/model/PayloadModel.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.controller.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class PayloadModel { 11 | private String value; 12 | } 13 | -------------------------------------------------------------------------------- /zola-messaging-server/src/main/java/com/github/dhslrl321/zsmq/controller/model/QueueNameModel.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.controller.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class QueueNameModel { 11 | private String value; 12 | } 13 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/converter/MessageConverter.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.converter; 2 | 3 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 4 | 5 | public interface MessageConverter extends Supportable { 6 | 7 | ZolaMessage toMessage(String queueName, Object payload); 8 | 9 | String fromMessage(ZolaMessage message); 10 | } 11 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/core/message/ZolaHeader.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.core.message; 2 | 3 | import com.github.dhslrl321.zsmq.core.queue.QueueName; 4 | import java.time.LocalDateTime; 5 | import lombok.Value; 6 | 7 | @Value(staticConstructor = "of") 8 | public class ZolaHeader { 9 | QueueName queueName; 10 | LocalDateTime timestamp; 11 | MediaTypes mediaType; 12 | } 13 | -------------------------------------------------------------------------------- /zola-messaging-server/src/main/java/com/github/dhslrl321/zsmq/controller/model/MessageModel.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.controller.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @AllArgsConstructor 9 | @NoArgsConstructor 10 | public class MessageModel { 11 | private HeaderModel header; 12 | private PayloadModel payload; 13 | } 14 | -------------------------------------------------------------------------------- /integration-tests/polling-queue-prod-cons/src/main/java/com/github/dhslrl321/zsmq/Main.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Main { 8 | public static void main(String[] args) { 9 | SpringApplication.run(Main.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /integration-tests/spring-boot-starter-integrations/src/main/java/com/github/dhslrl321/zsmq/Main.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Main { 8 | public static void main(String[] args) { 9 | SpringApplication.run(Main.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /integration-tests/stress/src/test/java/com/github/dhslrl321/zsmq/rpc/ResultReporter.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.rpc; 2 | 3 | public class ResultReporter { 4 | 5 | private static final String TOOK_MS_MESSAGE = "test took (ms) : "; 6 | 7 | public static void reportToConsole(long begin, long end) { 8 | long diff = (begin - end); 9 | System.out.println(TOOK_MS_MESSAGE + diff + " (ms)"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /zola-messaging-spring-sdk/README.md: -------------------------------------------------------------------------------- 1 | # ZSMQ Spring SDK 2 | 3 | this is zsmq spring sdk 4 | 5 | # ListeningTask 6 | 7 | image 8 | 9 | # Consuming in Spring 10 | 11 | image 12 | -------------------------------------------------------------------------------- /zola-messaging-spring-sdk/src/main/java/com/github/dhslrl321/zsmq/annotation/ZolaConsumer.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target(ElementType.TYPE) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface ZolaConsumer { 11 | } 12 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'zsmq' 2 | 3 | include 'zola-messaging-core' 4 | include 'zola-messaging-server' 5 | include 'zola-messaging-spring-sdk' 6 | include 'zola-messaging-spring-boot-starter' 7 | include 'zola-messaging-spring-boot-autoconfigure' 8 | 9 | include 'integration-tests' 10 | include 'integration-tests:stress' 11 | include 'integration-tests:polling-queue-prod-cons' 12 | include 'integration-tests:spring-boot-starter-integrations' 13 | 14 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/listener/strategy/ListeningStrategy.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.listener.strategy; 2 | 3 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 4 | import com.github.dhslrl321.zsmq.listener.ListeningInformation; 5 | 6 | public interface ListeningStrategy { 7 | ZolaMessage peek(ListeningInformation information); 8 | 9 | boolean acknowledgement(ListeningInformation information); 10 | } 11 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/listener/ListeningInformation.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.listener; 2 | 3 | import lombok.Value; 4 | 5 | @Value(staticConstructor = "of") 6 | public class ListeningInformation { 7 | String server; 8 | String queueName; 9 | DeletionPolicy deletionPolicy; 10 | 11 | public boolean isSameDeletionPolicy(DeletionPolicy policy) { 12 | return deletionPolicy.equals(policy); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/converter/MessageConverters.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.converter; 2 | 3 | public class MessageConverters { 4 | public static CompositeMessageConverter initConverters() { 5 | CompositeMessageConverter converters = new CompositeMessageConverter(); 6 | converters.register(new StringMessageConverter()); 7 | converters.register(new JsonMessageConverter()); 8 | return converters; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/detector/MessageListenerDetector.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.detector; 2 | 3 | import com.github.dhslrl321.zsmq.commons.Pair; 4 | import com.github.dhslrl321.zsmq.listener.ListeningInformation; 5 | import com.github.dhslrl321.zsmq.listener.MessageListener; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | public interface MessageListenerDetector { 10 | List> detect(); 11 | } 12 | -------------------------------------------------------------------------------- /zola-messaging-server/src/main/java/com/github/dhslrl321/zsmq/controller/model/HeaderModel.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.controller.model; 2 | 3 | import java.time.LocalDateTime; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | public class HeaderModel { 12 | private QueueNameModel queueName; 13 | private LocalDateTime timestamp; 14 | private String mediaType; 15 | } 16 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/listener/task/ListeningTaskFactory.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.listener.task; 2 | 3 | import com.github.dhslrl321.zsmq.commons.Pair; 4 | import com.github.dhslrl321.zsmq.listener.ListeningInformation; 5 | import com.github.dhslrl321.zsmq.listener.MessageListener; 6 | import java.util.List; 7 | 8 | public interface ListeningTaskFactory { 9 | List createBy(List> pairs); 10 | } 11 | -------------------------------------------------------------------------------- /zola-messaging-core/src/test/java/com/github/dhslrl321/zsmq/core/queue/AbstractZolaQueueTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.core.queue; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class AbstractZolaQueueTest { 8 | AbstractZolaQueue sut = ZolaSimpleQueue.newInstance(QueueName.of("hello")); 9 | 10 | @Test 11 | void name() { 12 | int size = sut.size(); 13 | 14 | assertThat(size).isZero(); 15 | } 16 | } -------------------------------------------------------------------------------- /dashboard/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | 21 | # debug 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | 26 | # local env files 27 | .env.local 28 | .env.development.local 29 | .env.test.local 30 | .env.production.local 31 | -------------------------------------------------------------------------------- /dashboard/styles/default-theme.js: -------------------------------------------------------------------------------- 1 | import { css } from "styled-components"; 2 | 3 | const color = { 4 | primary: "#5F56EF", 5 | hoverPrimary: "#4D43ED", 6 | activePrimary: "#372BFF", 7 | white: "#ffffff", 8 | deepBlue: "#122F4C", 9 | lightBlue: "#5094FA", 10 | placeholder: "#9E9E9E", 11 | dark: "#303740", 12 | defaultFont: "#525463", 13 | gray: "#9E9E9E", 14 | red: "#C93B3B", 15 | green: "#36B13B" 16 | } 17 | 18 | const defaultTheme = { 19 | color 20 | } 21 | 22 | export default defaultTheme; -------------------------------------------------------------------------------- /zola-messaging-server/src/main/java/com/github/dhslrl321/zsmq/config/ZolaBeanConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.config; 2 | 3 | import com.github.dhslrl321.zsmq.core.queue.ZolaQueueContainer; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Configuration 8 | public class ZolaBeanConfig { 9 | @Bean 10 | public ZolaQueueContainer zolaQueueContainer() { 11 | return new ZolaQueueContainer(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /integration-tests/stress/src/test/java/com/github/dhslrl321/zsmq/rpc/RpcCallDequeueTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.rpc; 2 | 3 | import com.github.dhslrl321.zsmq.core.queue.ZolaQueueContainer; 4 | import com.github.dhslrl321.zsmq.listener.ZolaMessageListeningProcessor; 5 | import org.junit.jupiter.api.DisplayName; 6 | import org.junit.jupiter.api.Test; 7 | 8 | public class RpcCallDequeueTest { 9 | ZolaMessageListeningProcessor sut; 10 | 11 | @Test 12 | void name() { 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /zola-messaging-spring-boot-autoconfigure/src/main/java/com/github/dhslrl321/zsmq/ZsmqProperty.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | @ConfigurationProperties(prefix = "zsmq") 10 | @Setter 11 | @Getter 12 | public class ZsmqProperty { 13 | private String url; 14 | private boolean listening; 15 | } 16 | -------------------------------------------------------------------------------- /dashboard/components/queue-table/presenter/styles.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const GreenDot = styled.div` 4 | width: 15px; 5 | height: 15px; 6 | background: #62bd19; 7 | border-radius: 50%; 8 | `; 9 | 10 | export const RedDot = styled.div` 11 | width: 15px; 12 | height: 15px; 13 | background: red; 14 | border-radius: 50%; 15 | `; 16 | 17 | export const DotContainer = styled.div` 18 | display: flex; 19 | justify-content: end; 20 | align-items: center; 21 | 22 | width: 100%; 23 | padding-right: 15px; 24 | `; 25 | -------------------------------------------------------------------------------- /zola-messaging-core/README.md: -------------------------------------------------------------------------------- 1 | # ZSMQ's Core 2 | 3 | # ZolaQueue 4 | 5 | image 6 | 7 | # Message 8 | 9 | image 10 | 11 | # Message Converting and Sending, publishing 12 | 13 | image 14 | -------------------------------------------------------------------------------- /dashboard/styles/mui-theme.js: -------------------------------------------------------------------------------- 1 | import { createTheme } from "@mui/material/styles"; 2 | 3 | const muiTheme = createTheme({ 4 | palette: { 5 | primary: { 6 | main: '#FFFFFF' 7 | }, 8 | secondary: { 9 | main: '#37404A' 10 | }, 11 | white: { 12 | main: "#fff" 13 | }, 14 | error: { 15 | main: '#C93B3B' 16 | }, 17 | text: { 18 | primary: '#525463', 19 | secondary: '#9E9E9E', 20 | disabled: '#808080', 21 | hint: '#000', 22 | myTextColor: '#000' 23 | } 24 | }, 25 | }); 26 | 27 | export default muiTheme; -------------------------------------------------------------------------------- /integration-tests/stress/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | 5 | group 'com.github.dhslrl321' 6 | version '1.0.0' 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | 14 | testImplementation project(':zola-messaging-core') 15 | testImplementation project(':zola-messaging-spring-sdk') 16 | 17 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' 18 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' 19 | } 20 | 21 | test { 22 | exclude 'com/github/dhslrl321/zsmq/**' 23 | useJUnitPlatform() 24 | } -------------------------------------------------------------------------------- /zola-messaging-spring-sdk/src/main/java/com/github/dhslrl321/zsmq/annotation/ZolaMessageListener.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.annotation; 2 | 3 | import com.github.dhslrl321.zsmq.listener.DeletionPolicy; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | @Target(ElementType.METHOD) 10 | @Retention(RetentionPolicy.RUNTIME) 11 | public @interface ZolaMessageListener { 12 | String queueName(); 13 | DeletionPolicy deletionPolicy(); 14 | } 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/core/queue/AbstractZolaQueue.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.core.queue; 2 | 3 | import java.time.LocalDateTime; 4 | import lombok.AllArgsConstructor; 5 | 6 | @AllArgsConstructor 7 | public abstract class AbstractZolaQueue implements ZolaQueue { 8 | 9 | private final QueueName name; 10 | private final LocalDateTime createdAt; 11 | 12 | @Override 13 | public QueueName getName() { 14 | return name; 15 | } 16 | 17 | @Override 18 | public LocalDateTime getCreatedAt() { 19 | return createdAt; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /zola-messaging-spring-sdk/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "io.freefair.lombok" version "6.5.1" 3 | id 'java-library' 4 | id 'maven-publish' 5 | } 6 | 7 | repositories { 8 | mavenCentral() 9 | } 10 | 11 | dependencies { 12 | 13 | implementation project(':zola-messaging-core') 14 | 15 | testImplementation 'org.assertj:assertj-core:3.23.1' 16 | testImplementation 'org.mockito:mockito-core:4.7.0' 17 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' 18 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' 19 | } 20 | 21 | test { 22 | useJUnitPlatform() 23 | } 24 | -------------------------------------------------------------------------------- /integration-tests/spring-boot-starter-integrations/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.7.4' 3 | id 'io.spring.dependency-management' version '1.0.14.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.github.dhslrl321.zsmq' 8 | 9 | repositories { 10 | mavenCentral() 11 | } 12 | 13 | dependencies { 14 | 15 | implementation 'org.springframework.boot:spring-boot-starter-web' 16 | 17 | testImplementation project(':zola-messaging-spring-boot-starter') 18 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 19 | } 20 | 21 | tasks.named('test') { 22 | useJUnitPlatform() 23 | } 24 | -------------------------------------------------------------------------------- /integration-tests/polling-queue-prod-cons/src/test/java/com/github/dhslrl321/zsmq/integration/FooZolaMessageListener.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.integration; 2 | 3 | import com.github.dhslrl321.zsmq.annotation.ZolaConsumer; 4 | import com.github.dhslrl321.zsmq.annotation.ZolaMessageListener; 5 | import com.github.dhslrl321.zsmq.listener.DeletionPolicy; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | @ZolaConsumer 10 | public class FooZolaMessageListener { 11 | @ZolaMessageListener(queueName = "TEST-QUEUE-NAME", deletionPolicy = DeletionPolicy.ALWAYS) 12 | public void listenForTest(String message) { 13 | System.out.print(message); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /dashboard/api-service/command-queue-service.js: -------------------------------------------------------------------------------- 1 | import API from "./api"; 2 | import {ERROR_NOT_FOUND} from "../commons/constants"; 3 | 4 | export const createQueueAPI = async (name) => { 5 | try { 6 | const {data} = await API.post( 7 | '/api/queues', 8 | JSON.stringify({ name }) 9 | ); 10 | return data; 11 | } catch { 12 | console.log('error when request zsmq server') 13 | } 14 | } 15 | 16 | export const deleteQueueAPI = async (name) => { 17 | try { 18 | const {data} = await API.delete( 19 | `/api/queues/${name}`, 20 | ); 21 | return data; 22 | } catch (e) { 23 | const {status} = e.response; 24 | if (status === 404) { 25 | return ERROR_NOT_FOUND; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /zola-messaging-spring-sdk/src/main/java/com/github/dhslrl321/zsmq/listener/SpringBeanMessageListener.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.listener; 2 | 3 | import java.lang.reflect.InvocationTargetException; 4 | import java.lang.reflect.Method; 5 | import lombok.Value; 6 | 7 | @Value(staticConstructor = "of") 8 | public class SpringBeanMessageListener implements MessageListener { 9 | 10 | Object object; 11 | Method method; 12 | 13 | @Override 14 | public void listen(String serialized) { 15 | try { 16 | method.invoke(object, serialized); 17 | } catch (IllegalAccessException | InvocationTargetException e) { 18 | e.printStackTrace(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /zola-messaging-spring-sdk/src/main/java/com/github/dhslrl321/zsmq/listener/task/ThreadPoolListeningExecutor.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.listener.task; 2 | 3 | import java.util.List; 4 | import java.util.concurrent.LinkedBlockingQueue; 5 | import java.util.concurrent.ThreadPoolExecutor; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | public class ThreadPoolListeningExecutor implements ListeningTaskExecutor { 9 | 10 | // TODO check proper setting 11 | private final ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 50, 10, TimeUnit.SECONDS, 12 | new LinkedBlockingQueue<>()); 13 | 14 | public void executeAll(List tasks) { 15 | tasks.forEach(executor::execute); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /dashboard/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next dev", 5 | "test": "mocha test/**/**.spec.js", 6 | "build": "next build", 7 | "start": "next start" 8 | }, 9 | "dependencies": { 10 | "@emotion/react": "^11.10.4", 11 | "@emotion/styled": "^11.10.4", 12 | "@material-ui/styles": "^4.11.5", 13 | "@mui/icons-material": "^5.10.6", 14 | "@mui/material": "^5.10.6", 15 | "axios": "^0.27.2", 16 | "next": "latest", 17 | "react": "17.0.2", 18 | "react-dom": "17.0.2", 19 | "styled-components": "^5.3.5", 20 | "styled-reset": "^4.4.2" 21 | }, 22 | "devDependencies": { 23 | "chai": "^4.3.6", 24 | "mocha": "^10.0.0", 25 | "prettier": "2.7.1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /zola-messaging-spring-boot-autoconfigure/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id "io.freefair.lombok" version "6.5.1" 4 | } 5 | 6 | repositories { 7 | mavenCentral() 8 | } 9 | 10 | dependencies { 11 | 12 | api project(':zola-messaging-core') 13 | api project(':zola-messaging-spring-sdk') 14 | 15 | implementation 'org.springframework.boot:spring-boot:2.7.2' 16 | implementation 'org.springframework.boot:spring-boot-configuration-processor:2.7.2' 17 | implementation 'org.springframework.boot:spring-boot-autoconfigure:2.7.2' 18 | 19 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' 20 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' 21 | } 22 | 23 | test { 24 | useJUnitPlatform() 25 | } -------------------------------------------------------------------------------- /zola-messaging-server/src/main/java/com/github/dhslrl321/ServerApplication.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | @SpringBootApplication 9 | public class ServerApplication { 10 | 11 | @RestController 12 | public static class HealthCheck { 13 | @GetMapping("/health") 14 | public String health() { 15 | return "ok"; 16 | } 17 | } 18 | 19 | public static void main(String[] args) { 20 | SpringApplication.run(ServerApplication.class); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /zola-messaging-spring-sdk/src/main/java/com/github/dhslrl321/zsmq/listener/task/PollingQueueListeningTaskFactory.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.listener.task; 2 | 3 | import com.github.dhslrl321.zsmq.commons.Pair; 4 | import com.github.dhslrl321.zsmq.listener.ListeningInformation; 5 | import com.github.dhslrl321.zsmq.listener.MessageListener; 6 | import com.github.dhslrl321.zsmq.listener.strategy.HttpPollingListeningStrategy; 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | 10 | public class PollingQueueListeningTaskFactory extends AbstractListeningTaskFactory { 11 | protected ListeningTask createBy(Pair pair) { 12 | return new ListeningTask(new HttpPollingListeningStrategy(), pair); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /dashboard/components/queue-table/container/index.js: -------------------------------------------------------------------------------- 1 | import TablePresenter from '../presenter'; 2 | 3 | import { useEffect, useState } from 'react'; 4 | import { getQueueDescribeAPI } from '../../../api-service/query-queue-service'; 5 | 6 | const TableContainer = () => { 7 | const [queues, setQueues] = useState([]); 8 | const [value, setValue] = useState(1); 9 | 10 | useEffect(() => { 11 | pollingQueue(); 12 | }, [value]); 13 | 14 | const pollingQueue = async () => { 15 | const { queueTotalSize, detail } = await getQueueDescribeAPI(); 16 | setQueues(detail); 17 | setTimeout(() => setValue(value + 1), 1000); 18 | }; 19 | 20 | return ( 21 | <> 22 | 23 | 24 | ); 25 | }; 26 | 27 | export default TableContainer; 28 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/core/message/ZolaMessage.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.core.message; 2 | 3 | import java.time.LocalDateTime; 4 | import lombok.Value; 5 | 6 | @Value(staticConstructor = "of") 7 | public class ZolaMessage { 8 | ZolaHeader header; 9 | ZolaPayload payload; 10 | 11 | public boolean isSameTypeBy(MediaTypes mediaTypes) { 12 | return header.getMediaType().equals(mediaTypes); 13 | } 14 | 15 | public String getQueueNameValue() { 16 | return header.getQueueName().getValue(); 17 | } 18 | 19 | public MediaTypes getMediaType() { 20 | return header.getMediaType(); 21 | } 22 | 23 | public LocalDateTime getTimestamp() { 24 | return header.getTimestamp(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /zola-messaging-spring-sdk/src/test/java/com/github/dhslrl321/zsmq/listener/strategy/fake/FakeHttpClient.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.listener.strategy.fake; 2 | 3 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 4 | import com.github.dhslrl321.zsmq.http.ZolaHttpClient; 5 | import java.util.Optional; 6 | 7 | public class FakeHttpClient extends ZolaHttpClient { 8 | @Override 9 | public boolean requestPush(String baseUrl, ZolaMessage message) { 10 | return true; 11 | } 12 | 13 | @Override 14 | public Optional requestPeek(String baseUrl, String queueName) { 15 | return Optional.empty(); 16 | } 17 | 18 | @Override 19 | public boolean acknowledgement(String server, String queueName) { 20 | return false; 21 | } 22 | } -------------------------------------------------------------------------------- /zola-messaging-server/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.7.3' 3 | id 'io.spring.dependency-management' version '1.0.13.RELEASE' 4 | id "io.freefair.lombok" version "6.5.1" 5 | id 'java' 6 | } 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | 14 | implementation project(':zola-messaging-core') 15 | 16 | implementation 'org.springframework.boot:spring-boot-starter-web' 17 | implementation 'org.apache.commons:commons-lang3:3.12.0' 18 | 19 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 20 | 21 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' 22 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' 23 | } 24 | 25 | test { 26 | useJUnitPlatform() 27 | } -------------------------------------------------------------------------------- /zola-messaging-server/src/test/java/com/github/dhslrl321/zsmq/BeanRegisterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import com.github.dhslrl321.zsmq.core.queue.ZolaQueueContainer; 6 | import org.junit.jupiter.api.Test; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.context.ApplicationContext; 10 | 11 | @SpringBootTest 12 | class BeanRegisterTest { 13 | 14 | @Autowired 15 | ApplicationContext context; 16 | 17 | @Test 18 | void name() { 19 | ZolaQueueContainer actual = context.getBean("zolaQueueContainer", ZolaQueueContainer.class); 20 | 21 | assertThat(actual).isNotNull(); 22 | } 23 | } -------------------------------------------------------------------------------- /zola-messaging-core/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id "io.freefair.lombok" version "6.5.1" 4 | } 5 | 6 | repositories { 7 | mavenCentral() 8 | } 9 | 10 | dependencies { 11 | implementation 'com.google.code.gson:gson:2.9.0' 12 | implementation 'org.apache.commons:commons-lang3:3.12.0' 13 | 14 | implementation 'com.squareup.okhttp3:okhttp:4.10.0' 15 | 16 | testImplementation 'org.mockito:mockito-core:4.7.0' 17 | testImplementation 'org.mockito:mockito-junit-jupiter:4.8.0' 18 | 19 | 20 | testImplementation 'org.assertj:assertj-core:3.23.1' 21 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' 22 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' 23 | implementation 'org.valid4j:valid4j:0.5.0' 24 | } 25 | 26 | test { 27 | useJUnitPlatform() 28 | } -------------------------------------------------------------------------------- /integration-tests/polling-queue-prod-cons/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'org.springframework.boot' version '2.7.4' 4 | id 'io.spring.dependency-management' version '1.0.14.RELEASE' 5 | } 6 | 7 | group 'com.github.dhslrl321' 8 | version '1.0.0' 9 | 10 | repositories { 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | 16 | implementation 'org.springframework.boot:spring-boot-starter-web' 17 | 18 | testImplementation project(':zola-messaging-spring-boot-starter') 19 | 20 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 21 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' 22 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' 23 | } 24 | 25 | test { 26 | exclude 'com.github.dhslrl321.zsmq.integration' 27 | useJUnitPlatform() 28 | } 29 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/listener/task/AbstractListeningTaskFactory.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.listener.task; 2 | 3 | import com.github.dhslrl321.zsmq.commons.Pair; 4 | import com.github.dhslrl321.zsmq.listener.ListeningInformation; 5 | import com.github.dhslrl321.zsmq.listener.MessageListener; 6 | import java.util.List; 7 | import java.util.stream.Collectors; 8 | 9 | public abstract class AbstractListeningTaskFactory implements ListeningTaskFactory { 10 | 11 | @Override 12 | public List createBy(List> pairs) { 13 | return pairs.stream() 14 | .map(this::createBy) 15 | .collect(Collectors.toList()); 16 | } 17 | 18 | protected abstract ListeningTask createBy(Pair pair); 19 | } 20 | -------------------------------------------------------------------------------- /dashboard/pages/_app.js: -------------------------------------------------------------------------------- 1 | import Head from "next/head"; 2 | import {ThemeProvider} from "styled-components"; 3 | import {ThemeProvider as MuiThemeProvider} from "@mui/material/styles"; 4 | 5 | import defaultTheme from "../styles/default-theme"; 6 | import muiTheme from "../styles/mui-theme"; 7 | import GlobalStyles from "../styles/global-theme"; 8 | import Menu from "../components/menu/presenter"; 9 | 10 | const MyApp = ({Component, pageProps}) => { 11 | return ( 12 | <> 13 | 14 | 15 | ZSMQ | Zola Simple Message Queue 16 | 17 | < MuiThemeProvider theme={muiTheme}> 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | ) 26 | } 27 | 28 | export default MyApp; -------------------------------------------------------------------------------- /zola-messaging-core/src/test/java/com/github/dhslrl321/zsmq/client/spy/SpyZolaHttpClient.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.client.spy; 2 | 3 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 4 | import com.github.dhslrl321.zsmq.http.ZolaHttpClient; 5 | import java.util.Optional; 6 | 7 | public class SpyZolaHttpClient extends ZolaHttpClient { 8 | 9 | public boolean pushCalled = false; 10 | 11 | @Override 12 | public boolean requestPush(String baseUrl, ZolaMessage message) { 13 | pushCalled = true; 14 | return true; 15 | } 16 | 17 | @Override 18 | public Optional requestPeek(String baseUrl, String queueName) { 19 | return super.requestPeek(baseUrl, queueName); 20 | } 21 | 22 | @Override 23 | public boolean acknowledgement(String server, String queueName) { 24 | return super.acknowledgement(server, queueName); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ## http://korea.gnu.org/manual/release/make/make-sjp/make-ko_toc.html 2 | .PHONY : help clean test build run pull push stop-db 3 | .DEFAULT : xxx 4 | 5 | PROFILE ?= local 6 | GIT_CURRENT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD) 7 | 8 | GRADLE_TASKS = clean test build 9 | 10 | ## https://gist.github.com/prwhite/8168133#gistcomment-3785627 11 | help: ## show help message 12 | @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[$$()% 0-9a-zA-Z_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) 13 | 14 | ${GRADLE_TASKS}: 15 | ifdef MODULE 16 | ./gradlew :$(MODULE):$@ 17 | else 18 | ./gradlew $@ 19 | endif 20 | 21 | clean: ## gradle clean 22 | 23 | test: clean ## gradle test 24 | 25 | build: clean ## gradle build 26 | 27 | run-server: java -jar zola-messaging-server/build/libs/zola-messaging-server-1.0.0.jar -------------------------------------------------------------------------------- /zola-messaging-core/src/test/java/com/github/dhslrl321/zsmq/commons/ZolaJsonSerializerTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.commons; 2 | 3 | import com.github.dhslrl321.zsmq.SharedFixture; 4 | import com.google.gson.Gson; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import org.junit.jupiter.api.Test; 8 | 9 | class ZolaJsonSerializerTest { 10 | 11 | @Data 12 | @AllArgsConstructor 13 | static class Foo { 14 | String name; 15 | Bar bar; 16 | } 17 | 18 | @Data 19 | @AllArgsConstructor 20 | static class Bar { 21 | String address; 22 | 23 | } 24 | 25 | @Test 26 | void name() { 27 | //String serialized = Serializer.serialize(SharedFixture.ANY_JSON_MESSAGE); 28 | Gson gson = new Gson(); 29 | String serialized = gson.toJson(SharedFixture.ANY_STRING_MESSAGE); 30 | System.out.println("serialized = " + serialized); 31 | } 32 | } -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/listener/ZolaMessageListeningProcessor.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.listener; 2 | 3 | import com.github.dhslrl321.zsmq.commons.Pair; 4 | import com.github.dhslrl321.zsmq.detector.MessageListenerDetector; 5 | import com.github.dhslrl321.zsmq.listener.task.ListeningTask; 6 | import com.github.dhslrl321.zsmq.listener.task.ListeningTaskExecutor; 7 | import com.github.dhslrl321.zsmq.listener.task.ListeningTaskFactory; 8 | import java.util.List; 9 | import lombok.RequiredArgsConstructor; 10 | 11 | @RequiredArgsConstructor 12 | public class ZolaMessageListeningProcessor { 13 | private boolean listening = false; 14 | private final MessageListenerDetector detector; 15 | private final ListeningTaskFactory taskFactory; 16 | private final ListeningTaskExecutor taskExecutor; 17 | 18 | public void doProcess() { 19 | listening = true; 20 | List> listeners = detector.detect(); 21 | List tasks = taskFactory.createBy(listeners); 22 | taskExecutor.executeAll(tasks); 23 | } 24 | 25 | public boolean isListening() { 26 | return listening; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/converter/StringMessageConverter.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.converter; 2 | 3 | import static com.github.dhslrl321.zsmq.core.message.MediaTypes.TEXT; 4 | 5 | import com.github.dhslrl321.zsmq.core.message.ZolaHeader; 6 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 7 | import com.github.dhslrl321.zsmq.core.message.ZolaPayload; 8 | import com.github.dhslrl321.zsmq.core.queue.QueueName; 9 | import java.time.LocalDateTime; 10 | 11 | class StringMessageConverter implements MessageConverter { 12 | @Override 13 | public ZolaMessage toMessage(String queueName, Object payload) { 14 | ZolaHeader header = ZolaHeader.of(QueueName.of(queueName), LocalDateTime.now(), TEXT); 15 | return ZolaMessage.of(header, ZolaPayload.of((String) payload)); 16 | } 17 | 18 | @Override 19 | public String fromMessage(ZolaMessage message) { 20 | return message.getPayload().getValue(); 21 | } 22 | 23 | @Override 24 | public boolean isSupport(Object payload) { 25 | return payload instanceof String; 26 | } 27 | 28 | @Override 29 | public boolean isSupport(ZolaMessage message) { 30 | return message.isSameTypeBy(TEXT); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /integration-tests/polling-queue-prod-cons/src/test/java/com/github/dhslrl321/zsmq/integration/BeforeTestConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.integration; 2 | 3 | import com.github.dhslrl321.zsmq.client.ZolaClientConfig; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import javax.annotation.PostConstruct; 7 | import javax.annotation.PreDestroy; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.web.client.RestTemplate; 11 | 12 | @Configuration 13 | public class BeforeTestConfiguration { 14 | 15 | public static final String TEST_QUEUE_NAME = "TEST-QUEUE-NAME"; 16 | 17 | @Autowired 18 | ZolaClientConfig zolaClientConfig; 19 | RestTemplate rest = new RestTemplate(); 20 | 21 | @PostConstruct 22 | public void makeQueueBeforeConstruct() { 23 | Map map = new HashMap<>(); 24 | map.put("name", TEST_QUEUE_NAME); 25 | 26 | rest.postForObject(zolaClientConfig.getServerBaseUrl() + "/api/queues", map, Map.class); 27 | } 28 | 29 | @PreDestroy 30 | public void cleanUpQueue() { 31 | rest.delete(zolaClientConfig.getServerBaseUrl() + "/api/queues/" + TEST_QUEUE_NAME); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /zola-messaging-core/src/test/java/com/github/dhslrl321/zsmq/listener/ZolaMessageListeningProcessorTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.listener; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.junit.jupiter.api.Assertions.*; 5 | import static org.mockito.Mockito.mock; 6 | 7 | import com.github.dhslrl321.zsmq.detector.MessageListenerDetector; 8 | import com.github.dhslrl321.zsmq.listener.task.ListeningTaskExecutor; 9 | import com.github.dhslrl321.zsmq.listener.task.ListeningTaskFactory; 10 | import org.junit.jupiter.api.Test; 11 | import org.junit.jupiter.api.extension.ExtendWith; 12 | import org.mockito.InjectMocks; 13 | import org.mockito.Mock; 14 | import org.mockito.junit.jupiter.MockitoExtension; 15 | 16 | @ExtendWith(MockitoExtension.class) 17 | class ZolaMessageListeningProcessorTest { 18 | 19 | @InjectMocks 20 | ZolaMessageListeningProcessor sut; 21 | 22 | @Mock 23 | MessageListenerDetector detector; 24 | @Mock 25 | ListeningTaskFactory factory; 26 | @Mock 27 | ListeningTaskExecutor executor; 28 | 29 | @Test 30 | void when_doProcess_is_called() { 31 | assertThat(sut.isListening()).isFalse(); 32 | 33 | sut.doProcess(); 34 | 35 | assertThat(sut.isListening()).isTrue(); 36 | } 37 | } -------------------------------------------------------------------------------- /zola-messaging-spring-sdk/src/main/java/com/github/dhslrl321/zsmq/listener/strategy/HttpPollingListeningStrategy.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.listener.strategy; 2 | 3 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 4 | import com.github.dhslrl321.zsmq.http.ZolaHttpClient; 5 | import com.github.dhslrl321.zsmq.listener.ListeningInformation; 6 | import java.util.Optional; 7 | import lombok.RequiredArgsConstructor; 8 | 9 | @RequiredArgsConstructor 10 | public class HttpPollingListeningStrategy implements ListeningStrategy { 11 | 12 | private ZolaHttpClient httpClient = new ZolaHttpClient(); 13 | 14 | @Override 15 | public ZolaMessage peek(ListeningInformation information) { 16 | Optional optionalMessage = httpClient.requestPeek(information.getServer(), information.getQueueName()); 17 | 18 | if (optionalMessage.isEmpty()) { 19 | return null; 20 | } 21 | 22 | return optionalMessage.get(); 23 | } 24 | 25 | @Override 26 | public boolean acknowledgement(ListeningInformation information) { 27 | return httpClient.acknowledgement(information.getServer(), information.getQueueName()); 28 | } 29 | 30 | protected void setHttpClient(ZolaHttpClient fakeHttpClient) { 31 | httpClient = fakeHttpClient; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /zola-messaging-server/src/main/java/com/github/dhslrl321/zsmq/controller/model/ModelConverter.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.controller.model; 2 | 3 | import com.github.dhslrl321.zsmq.core.message.MediaTypes; 4 | import com.github.dhslrl321.zsmq.core.message.ZolaHeader; 5 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 6 | import com.github.dhslrl321.zsmq.core.message.ZolaPayload; 7 | import com.github.dhslrl321.zsmq.core.queue.QueueName; 8 | 9 | public class ModelConverter { 10 | public static ZolaMessage convert(MessageModel model) { 11 | HeaderModel headerModel = model.getHeader(); 12 | ZolaHeader zolaHeader = ZolaHeader.of(QueueName.of(headerModel.getQueueName().getValue()), 13 | headerModel.getTimestamp(), 14 | MediaTypes.valueOf(headerModel.getMediaType())); 15 | ZolaPayload zolaPayload = ZolaPayload.of(model.getPayload().getValue()); 16 | return ZolaMessage.of(zolaHeader, zolaPayload); 17 | } 18 | 19 | public static MessageModel convert(ZolaMessage zolaMessage) { 20 | HeaderModel headerModel = new HeaderModel(new QueueNameModel(zolaMessage.getQueueNameValue()), 21 | zolaMessage.getTimestamp(), zolaMessage.getMediaType().name()); 22 | return new MessageModel(headerModel, new PayloadModel(zolaMessage.getPayload().getValue())); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/core/queue/ZolaSimpleQueue.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.core.queue; 2 | 3 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 4 | import com.github.dhslrl321.zsmq.exception.EmptyQueueException; 5 | import java.time.LocalDateTime; 6 | import java.util.LinkedList; 7 | import java.util.Queue; 8 | 9 | public class ZolaSimpleQueue extends AbstractZolaQueue { 10 | 11 | public static ZolaSimpleQueue newInstance(QueueName queueName) { 12 | return new ZolaSimpleQueue(queueName); 13 | } 14 | 15 | private final Queue queue = new LinkedList<>(); 16 | 17 | private ZolaSimpleQueue(QueueName name) { 18 | super(name, LocalDateTime.now()); 19 | } 20 | 21 | @Override 22 | public ZolaMessage peek() { 23 | return queue.peek(); 24 | } 25 | 26 | @Override 27 | public ZolaMessage pop() { 28 | throwWhenEmpty(); 29 | return queue.poll(); 30 | } 31 | 32 | @Override 33 | public void push(ZolaMessage zolaMessage) { 34 | queue.offer(zolaMessage); 35 | } 36 | 37 | @Override 38 | public int size() { 39 | return queue.size(); 40 | } 41 | 42 | private void throwWhenEmpty() { 43 | if (queue.isEmpty()) { 44 | throw new EmptyQueueException("Zola Queue is empty!"); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /dashboard/styles/global-theme.js: -------------------------------------------------------------------------------- 1 | import reset from "styled-reset"; 2 | import { createGlobalStyle } from "styled-components"; 3 | 4 | const GlobalStyle = createGlobalStyle` 5 | ${reset} 6 | * { 7 | box-sizing: border-box; 8 | } 9 | body { 10 | font-family: -apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif; 11 | background: #F8F9FC; 12 | color: #525463; 13 | } 14 | a { 15 | color: inherit; 16 | text-decoration: none; 17 | } 18 | input, button { 19 | background-color: transparent; 20 | border: none; 21 | outline: none; 22 | } 23 | h1, h2, h3, h4, h5, h6{ 24 | font-family:'Maven Pro', sans-serif; 25 | } 26 | ul, ol, li, a, input, select, textarea { 27 | margin: 0; 28 | padding: 0; 29 | border: 0 none; 30 | } 31 | ul, ol, li { 32 | list-style: none; 33 | } 34 | em, address { 35 | font-style: normal; 36 | } 37 | img { 38 | border: 0 none; 39 | font-size: 0; 40 | line-height: 0; 41 | } 42 | sup { 43 | position: relative; 44 | top: 2px; 45 | font-size: 11px; 46 | line-height: 100%; 47 | } 48 | 49 | /* @media only screen and (max-width: 768px) { 50 | body { 51 | font-size: 12px; 52 | } 53 | } 54 | @media only screen and (max-width: 576px) { 55 | body { 56 | font-size: 10px; 57 | } 58 | } */ 59 | `; 60 | 61 | export default GlobalStyle; -------------------------------------------------------------------------------- /integration-tests/spring-boot-starter-integrations/src/test/java/com/github/dhslrl321/zsmq/integration/ProducingModeTests.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.integration; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import com.github.dhslrl321.zsmq.client.ZolaClientConfig; 6 | import com.github.dhslrl321.zsmq.client.ZolaQueueMessageTemplate; 7 | import com.github.dhslrl321.zsmq.listener.ZolaMessageListeningProcessor; 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.beans.factory.annotation.Value; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.test.context.ActiveProfiles; 13 | 14 | @SpringBootTest 15 | @ActiveProfiles(value = "producing") 16 | class ProducingModeTests { 17 | 18 | @Autowired 19 | ZolaClientConfig zolaClientConfig; 20 | 21 | @Autowired 22 | ZolaQueueMessageTemplate zolaQueueMessageTemplate; 23 | 24 | @Autowired 25 | ZolaMessageListeningProcessor processor; 26 | 27 | 28 | @Value("${zsmq.listening}") 29 | private boolean LISTENING_FROM_PROPS; 30 | 31 | @Test 32 | void bean_creation_test_ListeningProcessor() { 33 | assertThatSpringBean(processor); 34 | 35 | assertThat(processor.isListening()).isEqualTo(LISTENING_FROM_PROPS); 36 | } 37 | 38 | private void assertThatSpringBean(Object bean) { 39 | assertThat(bean).isNotNull(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /dashboard/pages/_document.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Document, { Html, Head, Main, NextScript } from 'next/document'; 3 | import { ServerStyleSheet } from 'styled-components' 4 | import { ServerStyleSheets } from '@material-ui/styles'; 5 | 6 | class MyDocument extends Document { 7 | static async getInitialProps(ctx) { 8 | const styledComponentsSheet = new ServerStyleSheet() 9 | const materialSheets = new ServerStyleSheets() 10 | const originalRenderPage = ctx.renderPage; 11 | 12 | try { 13 | ctx.renderPage = () => originalRenderPage({ 14 | enhanceApp: App => 15 | props => styledComponentsSheet.collectStyles(materialSheets.collect()) 16 | }) 17 | const initialProps = await Document.getInitialProps(ctx) 18 | return { 19 | ...initialProps, 20 | styles: ( 21 | 22 | {initialProps.styles} 23 | {materialSheets.getStyleElement()} 24 | {styledComponentsSheet.getStyleElement()} 25 | 26 | ) 27 | } 28 | } finally { 29 | styledComponentsSheet.seal() 30 | } 31 | } 32 | 33 | render() { 34 | return ( 35 | 36 | 37 | 38 | 39 | 40 |
41 | 42 | 43 | 44 | ); 45 | } 46 | } 47 | 48 | MyDocument.displayName = "MyDocument"; 49 | 50 | export default MyDocument; -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/converter/CompositeMessageConverter.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.converter; 2 | 3 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | class CompositeMessageConverter implements MessageConverter, Registrable { 8 | private final List registeredConverters = new ArrayList<>(); 9 | 10 | @Override 11 | public ZolaMessage toMessage(String queueName, Object payload) { 12 | MessageConverter converter = registeredConverters.stream() 13 | .filter(i -> i.isSupport(payload)) 14 | .findFirst() 15 | .orElseThrow(); 16 | return converter.toMessage(queueName, payload); 17 | } 18 | 19 | @Override 20 | public String fromMessage(ZolaMessage message) { 21 | MessageConverter converter = registeredConverters.stream() 22 | .filter(i -> i.isSupport(message)) 23 | .findFirst() 24 | .orElseThrow(); 25 | return converter.fromMessage(message); 26 | } 27 | 28 | @Override 29 | public void register(MessageConverter converter) { 30 | registeredConverters.add(converter); 31 | } 32 | 33 | @Override 34 | public boolean isSupport(Object payload) { 35 | throw new UnsupportedOperationException("registrar does not provides isSupport() operation"); 36 | } 37 | 38 | @Override 39 | public boolean isSupport(ZolaMessage message) { 40 | throw new UnsupportedOperationException("registrar does not provides isSupport() operation"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/converter/JsonMessageConverter.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.converter; 2 | 3 | import static com.github.dhslrl321.zsmq.core.message.MediaTypes.JSON; 4 | 5 | import com.github.dhslrl321.zsmq.commons.ZolaJsonSerializer; 6 | import com.github.dhslrl321.zsmq.core.message.MediaTypes; 7 | import com.github.dhslrl321.zsmq.core.message.ZolaHeader; 8 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 9 | import com.github.dhslrl321.zsmq.core.message.ZolaPayload; 10 | import com.github.dhslrl321.zsmq.core.queue.QueueName; 11 | import java.time.LocalDateTime; 12 | 13 | class JsonMessageConverter implements MessageConverter { 14 | 15 | @Override 16 | public ZolaMessage toMessage(String queueName, Object payload) { 17 | ZolaHeader header = ZolaHeader.of(QueueName.of(queueName), LocalDateTime.now(), MediaTypes.JSON); 18 | String serialized = ZolaJsonSerializer.getInstance().serialize(payload); 19 | ZolaPayload zolaPayload = ZolaPayload.of(serialized); 20 | return ZolaMessage.of(header, zolaPayload); 21 | } 22 | 23 | @Override 24 | public String fromMessage(ZolaMessage message) { 25 | return message.getPayload().getValue(); 26 | } 27 | 28 | @Override 29 | public boolean isSupport(Object payload) { 30 | // Always true if not string. Because we only support json or simple string 31 | if (payload instanceof String) { 32 | return false; 33 | } 34 | return true; 35 | } 36 | 37 | @Override 38 | public boolean isSupport(ZolaMessage message) { 39 | return message.isSameTypeBy(JSON); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /dashboard/components/command-dialog/presenter/index.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import Button from '@mui/material/Button'; 3 | import TextField from '@mui/material/TextField'; 4 | import Dialog from '@mui/material/Dialog'; 5 | import DialogActions from '@mui/material/DialogActions'; 6 | import DialogContent from '@mui/material/DialogContent'; 7 | import DialogContentText from '@mui/material/DialogContentText'; 8 | import DialogTitle from '@mui/material/DialogTitle'; 9 | 10 | export default function Modal({ 11 | open, 12 | handleClickOpen, 13 | handleClose, 14 | title, 15 | content, 16 | text, 17 | onChangeInput, 18 | onClickSubmit, 19 | }) { 20 | return ( 21 |
22 | 25 | 26 | {title} 27 | 28 | {content} 29 | 40 | 41 | 42 | 45 | 48 | 49 | 50 |
51 | ); 52 | } 53 | -------------------------------------------------------------------------------- /integration-tests/stress/src/test/java/com/github/dhslrl321/zsmq/rpc/RpcCallEnqueueTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.rpc; 2 | 3 | import static org.junit.jupiter.api.Assertions.fail; 4 | 5 | import com.github.dhslrl321.zsmq.client.ZolaClientConfig; 6 | import com.github.dhslrl321.zsmq.client.ZolaQueueMessageTemplate; 7 | import com.github.dhslrl321.zsmq.http.ZolaHttpClient; 8 | import java.util.stream.IntStream; 9 | import org.junit.jupiter.api.AfterEach; 10 | import org.junit.jupiter.api.BeforeEach; 11 | import org.junit.jupiter.api.Disabled; 12 | import org.junit.jupiter.api.DisplayName; 13 | import org.junit.jupiter.api.Order; 14 | import org.junit.jupiter.api.Test; 15 | 16 | public class RpcCallEnqueueTest { 17 | 18 | private static final int SIZE = 1_000_000; 19 | 20 | ZolaQueueMessageTemplate sut; 21 | 22 | long start; 23 | long end; 24 | 25 | @BeforeEach 26 | void setUp() { 27 | sut = new ZolaQueueMessageTemplate( 28 | new ZolaClientConfig("http://localhost:8291"), new ZolaHttpClient()); 29 | 30 | start = System.currentTimeMillis(); 31 | } 32 | 33 | @AfterEach 34 | void tearDown() { 35 | end = System.currentTimeMillis(); 36 | ResultReporter.reportToConsole(end, start); 37 | } 38 | 39 | @Test 40 | @DisplayName("rpc call 1,000,000 messages") 41 | @Disabled 42 | void TEST() { 43 | IntStream.range(0, SIZE) 44 | .forEach((i) -> sut.convertAndSend("TESTING-QUEUE", "dummy message, " + i)); 45 | } 46 | 47 | @Test 48 | @Order(0) 49 | @Disabled 50 | void cannot_be_triggered_by_automatic_build() { 51 | fail("not support"); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /zola-messaging-server/src/main/java/com/github/dhslrl321/zsmq/controller/handler/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.controller.handler; 2 | 3 | import com.github.dhslrl321.zsmq.controller.model.SimpleResponse; 4 | import com.github.dhslrl321.zsmq.exception.EmptyQueueException; 5 | import com.github.dhslrl321.zsmq.exception.QueueNotFoundException; 6 | import java.util.NoSuchElementException; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.web.bind.annotation.ExceptionHandler; 9 | import org.springframework.web.bind.annotation.ResponseStatus; 10 | import org.springframework.web.bind.annotation.RestControllerAdvice; 11 | 12 | @RestControllerAdvice 13 | public class GlobalExceptionHandler { 14 | 15 | @ExceptionHandler(IllegalArgumentException.class) 16 | @ResponseStatus(HttpStatus.BAD_REQUEST) 17 | public SimpleResponse handle(IllegalArgumentException e) { 18 | return new SimpleResponse(e.getMessage()); 19 | } 20 | 21 | @ExceptionHandler(NoSuchElementException.class) 22 | @ResponseStatus(HttpStatus.BAD_REQUEST) 23 | public SimpleResponse handle(NoSuchElementException e) { 24 | return new SimpleResponse(e.getMessage()); 25 | } 26 | 27 | @ExceptionHandler(QueueNotFoundException.class) 28 | @ResponseStatus(HttpStatus.NOT_FOUND) 29 | public SimpleResponse handle(QueueNotFoundException e) { 30 | return new SimpleResponse(e.getMessage()); 31 | } 32 | 33 | @ExceptionHandler(EmptyQueueException.class) 34 | @ResponseStatus(HttpStatus.BAD_REQUEST) 35 | public SimpleResponse handle(EmptyQueueException e) { 36 | return new SimpleResponse(e.getMessage()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /zola-messaging-core/src/test/java/com/github/dhslrl321/zsmq/converter/StringMessageConverterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.converter; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import com.github.dhslrl321.zsmq.core.message.MediaTypes; 6 | import com.github.dhslrl321.zsmq.core.message.ZolaHeader; 7 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 8 | import com.github.dhslrl321.zsmq.core.message.ZolaPayload; 9 | import com.github.dhslrl321.zsmq.core.queue.QueueName; 10 | import java.time.LocalDateTime; 11 | import org.junit.jupiter.api.Test; 12 | 13 | class StringMessageConverterTest { 14 | 15 | StringMessageConverter sut = new StringMessageConverter(); 16 | 17 | static class Foo { 18 | String bar; 19 | } 20 | 21 | @Test 22 | void ifStringType() { 23 | boolean actual = sut.isSupport(""); 24 | 25 | assertThat(actual).isTrue(); 26 | } 27 | 28 | @Test 29 | void ifNotStringType() { 30 | boolean actual = sut.isSupport(new Foo()); 31 | 32 | assertThat(actual).isFalse(); 33 | } 34 | 35 | @Test 36 | void convert_to_string() { 37 | ZolaMessage actual = sut.toMessage("any name", "any payload"); 38 | 39 | assertThat(actual.getPayload().getValue()).isEqualTo("any payload"); 40 | } 41 | 42 | @Test 43 | void convert_from_message() { 44 | ZolaMessage message = ZolaMessage.of(ZolaHeader.of(QueueName.of("any queue name"), LocalDateTime.now(), MediaTypes.TEXT), 45 | ZolaPayload.of("any payload")); 46 | 47 | String actual = sut.fromMessage(message); 48 | 49 | assertThat(actual).isEqualTo("any payload"); 50 | } 51 | } -------------------------------------------------------------------------------- /dashboard/test/commons/validator.spec.js: -------------------------------------------------------------------------------- 1 | import { regexNumberAndEnglishAndHyphen } from '../../commons/validator.js'; 2 | import { expect } from 'chai'; 3 | 4 | describe('validation test', () => { 5 | it('success on english', () => { 6 | const actual = regexNumberAndEnglishAndHyphen('foo'); 7 | 8 | expect(actual).to.true; 9 | }); 10 | 11 | it('success on dash', () => { 12 | const actual = regexNumberAndEnglishAndHyphen('-'); 13 | 14 | expect(actual).to.true; 15 | }); 16 | 17 | it('success on number', () => { 18 | const actual = regexNumberAndEnglishAndHyphen('123'); 19 | 20 | expect(actual).to.true; 21 | }); 22 | }); 23 | 24 | describe('composite word validation test', () => { 25 | it('success on english and dash', () => { 26 | const actual = regexNumberAndEnglishAndHyphen('foo'); 27 | 28 | expect(actual).to.true; 29 | }); 30 | 31 | it('success on dash', () => { 32 | const actual = regexNumberAndEnglishAndHyphen('foo-'); 33 | 34 | expect(actual).to.true; 35 | }); 36 | 37 | it('success on english and dash and number', () => { 38 | const actual = regexNumberAndEnglishAndHyphen('foo-123'); 39 | 40 | expect(actual).to.true; 41 | }); 42 | }); 43 | 44 | describe('failure', () => { 45 | it('fail on korean', () => { 46 | const actual = regexNumberAndEnglishAndHyphen('안녕'); 47 | 48 | expect(actual).to.false; 49 | }); 50 | 51 | it('fail on special symbol', () => { 52 | const actual = regexNumberAndEnglishAndHyphen('@!#$%^&*_-'); 53 | 54 | expect(actual).to.false; 55 | }); 56 | 57 | it('fail on space', () => { 58 | const actual = regexNumberAndEnglishAndHyphen(' '); 59 | 60 | expect(actual).to.false; 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/client/ZolaQueueMessageTemplate.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.client; 2 | 3 | import static org.valid4j.Validation.validate; 4 | 5 | import com.github.dhslrl321.zsmq.converter.MessageConverter; 6 | import com.github.dhslrl321.zsmq.converter.MessageConverters; 7 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 8 | import com.github.dhslrl321.zsmq.http.ZolaHttpClient; 9 | import com.github.dhslrl321.zsmq.http.ZolaServerConnectionFailedException; 10 | import java.util.Objects; 11 | import lombok.RequiredArgsConstructor; 12 | import org.apache.commons.lang3.StringUtils; 13 | 14 | @RequiredArgsConstructor 15 | public class ZolaQueueMessageTemplate { 16 | 17 | private final ZolaClientConfig config; 18 | private final ZolaHttpClient httpClient; 19 | private final MessageConverter converter = MessageConverters.initConverters(); 20 | 21 | public void convertAndSend(String queueName, Object payload) { 22 | validate(!StringUtils.isBlank(queueName), new IllegalArgumentException("queue name must not be null or blank")); 23 | validate(!Objects.isNull(payload), new IllegalArgumentException("payload must not be null")); 24 | 25 | ZolaMessage message = converter.toMessage(queueName, payload); 26 | send(message); 27 | } 28 | 29 | private void send(ZolaMessage message) { 30 | boolean send = request(message); 31 | if (!send) { 32 | throw new ZolaServerConnectionFailedException("message produce failed"); 33 | } 34 | } 35 | 36 | private boolean request(ZolaMessage message) { 37 | return httpClient.requestPush(config.getServerBaseUrl(), message); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /zola-messaging-core/src/test/java/com/github/dhslrl321/zsmq/converter/JsonMessageConverterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.converter; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import com.github.dhslrl321.zsmq.SharedFixture; 6 | import com.github.dhslrl321.zsmq.core.message.MediaTypes; 7 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 8 | import org.junit.jupiter.api.DisplayName; 9 | import org.junit.jupiter.api.Test; 10 | 11 | class JsonMessageConverterTest { 12 | JsonMessageConverter sut = new JsonMessageConverter(); 13 | 14 | @Test 15 | void only_object_type() { 16 | assertThat(sut.isSupport(new SharedFixture.Foo("hello"))).isTrue(); 17 | } 18 | 19 | @Test 20 | void if_not_object() { 21 | assertThat(sut.isSupport("hello")).isFalse(); 22 | } 23 | 24 | @Test 25 | void can_convert() { 26 | ZolaMessage actual = sut.toMessage("ANY_QUEUE", new SharedFixture.Foo("hello")); 27 | 28 | assertThat(actual.getHeader().getMediaType()).isEqualTo(MediaTypes.JSON); 29 | assertThat(actual.getPayload().getValue()).isEqualTo("{\"bar\":\"hello\"}"); 30 | 31 | System.out.println("actual = " + actual); 32 | } 33 | 34 | @Test 35 | @DisplayName("payload -> message") 36 | void convert_to_json() { 37 | assertThat(sut.isSupport("bar")).isFalse(); 38 | assertThat(sut.isSupport(new SharedFixture.Foo("bar"))).isTrue(); 39 | } 40 | 41 | @Test 42 | @DisplayName("message -> payload") 43 | void toString_Test() { 44 | ZolaMessage message = sut.toMessage("SOME_QUEUE", new SharedFixture.Foo("hello")); 45 | String actual = sut.fromMessage(message); 46 | 47 | assertThat(actual).isEqualTo("{\"bar\":\"hello\"}"); 48 | } 49 | } -------------------------------------------------------------------------------- /dashboard/components/queue-table/presenter/index.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import Table from '@mui/material/Table'; 3 | import TableBody from '@mui/material/TableBody'; 4 | import TableCell from '@mui/material/TableCell'; 5 | import TableContainer from '@mui/material/TableContainer'; 6 | import TableHead from '@mui/material/TableHead'; 7 | import TableRow from '@mui/material/TableRow'; 8 | import Paper from '@mui/material/Paper'; 9 | 10 | import * as S from './styles'; 11 | 12 | export default function BasicTable({ queues }) { 13 | return ( 14 | 15 | 16 | 17 | 18 | Queue Name 19 | messagesAvailable 20 | createdAt 21 | status 22 | 23 | 24 | 25 | {queues.map((queue) => ( 26 | 27 | 28 | {queue.name} 29 | 30 | {queue.messagesAvailable} 31 | {queue.created} 32 | 33 | 34 | {/*{queue.health ? : }*/} 35 | 36 | 37 | 38 | 39 | ))} 40 | 41 |
42 |
43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /zola-messaging-server/src/main/java/com/github/dhslrl321/zsmq/controller/QueryQueueController.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.controller; 2 | 3 | import com.github.dhslrl321.zsmq.controller.model.MessageModel; 4 | import com.github.dhslrl321.zsmq.controller.model.ModelConverter; 5 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 6 | import com.github.dhslrl321.zsmq.core.queue.QueueDescribe; 7 | import com.github.dhslrl321.zsmq.core.queue.QueueName; 8 | import com.github.dhslrl321.zsmq.core.queue.ZolaQueueContainer; 9 | import java.util.Objects; 10 | import lombok.RequiredArgsConstructor; 11 | import org.springframework.http.ResponseEntity; 12 | import org.springframework.web.bind.annotation.CrossOrigin; 13 | import org.springframework.web.bind.annotation.GetMapping; 14 | import org.springframework.web.bind.annotation.PathVariable; 15 | import org.springframework.web.bind.annotation.RequestMapping; 16 | import org.springframework.web.bind.annotation.RestController; 17 | 18 | @RestController 19 | @RequestMapping(value = "/api") 20 | @RequiredArgsConstructor 21 | @CrossOrigin("*") 22 | public class QueryQueueController { 23 | 24 | private final ZolaQueueContainer container; 25 | 26 | @GetMapping("/queues/{name}") 27 | public ResponseEntity peekHead(@PathVariable String name) { 28 | ZolaMessage zolaMessage = container.peekBy(QueueName.of(name)); 29 | if (Objects.isNull(zolaMessage)) { 30 | return noContent(); 31 | } 32 | return ResponseEntity.ok(ModelConverter.convert(zolaMessage)); 33 | } 34 | 35 | @GetMapping("/queues") 36 | public ResponseEntity getQueues() { 37 | return ResponseEntity.ok(container.describe()); 38 | } 39 | 40 | private ResponseEntity noContent() { 41 | return ResponseEntity.noContent().build(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /zola-messaging-spring-sdk/src/test/java/com/github/dhslrl321/zsmq/listener/task/PollingQueueListeningTaskFactoryTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.listener.task; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import com.github.dhslrl321.zsmq.commons.Pair; 6 | import com.github.dhslrl321.zsmq.listener.DeletionPolicy; 7 | import com.github.dhslrl321.zsmq.listener.ListeningInformation; 8 | import com.github.dhslrl321.zsmq.listener.MessageListener; 9 | import com.github.dhslrl321.zsmq.listener.SpringBeanMessageListener; 10 | import java.util.List; 11 | import org.junit.jupiter.api.BeforeEach; 12 | import org.junit.jupiter.api.Test; 13 | 14 | class PollingQueueListeningTaskFactoryTest { 15 | 16 | private static final SpringBeanMessageListener LISTENER = SpringBeanMessageListener.of(null, null); 17 | private static final ListeningInformation LISTENING_INFORMATION = ListeningInformation.of("", "", 18 | DeletionPolicy.ALWAYS); 19 | 20 | PollingQueueListeningTaskFactory sut; 21 | 22 | @BeforeEach 23 | void setUp() { 24 | sut = new PollingQueueListeningTaskFactory(); 25 | } 26 | 27 | @Test 28 | void can_create_by_pair() { 29 | ListeningTask actual = sut.createBy(singlePair()); 30 | 31 | assertThat(actual.getListeningPair().getRight()).isEqualTo(LISTENING_INFORMATION); 32 | } 33 | 34 | @Test 35 | void can_create_by_pair_list() { 36 | List actual = sut.createBy(multiplePair()); 37 | 38 | assertThat(actual.size()).isEqualTo(2); 39 | } 40 | 41 | private List> multiplePair() { 42 | return List.of(singlePair(), singlePair()); 43 | } 44 | 45 | private Pair singlePair() { 46 | return Pair.of(LISTENER, LISTENING_INFORMATION); 47 | } 48 | } -------------------------------------------------------------------------------- /integration-tests/spring-boot-starter-integrations/src/test/java/com/github/dhslrl321/zsmq/integration/BeanAutoCreationTests.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.integration; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import com.github.dhslrl321.zsmq.client.ZolaClientConfig; 6 | import com.github.dhslrl321.zsmq.client.ZolaQueueMessageTemplate; 7 | import com.github.dhslrl321.zsmq.listener.ZolaMessageListeningProcessor; 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.beans.factory.annotation.Value; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.context.ApplicationContext; 13 | import org.springframework.test.context.ActiveProfiles; 14 | 15 | @SpringBootTest 16 | @ActiveProfiles(value = "consuming") 17 | class BeanAutoCreationTests { 18 | 19 | @Autowired 20 | ZolaClientConfig zolaClientConfig; 21 | 22 | @Autowired 23 | ZolaQueueMessageTemplate zolaQueueMessageTemplate; 24 | 25 | @Autowired 26 | ZolaMessageListeningProcessor processor; 27 | 28 | @Value("${zsmq.url}") 29 | private String URL_FROM_PROPS; 30 | 31 | @Test 32 | void bean_creation_test_ZolaClientConfig() { 33 | assertThatSpringBean(zolaClientConfig); 34 | assertThat(zolaClientConfig.getServerBaseUrl()).isEqualTo(URL_FROM_PROPS); 35 | } 36 | 37 | @Test 38 | void bean_creation_test_ZolaQueueMessageTemplate() { 39 | assertThatSpringBean(zolaQueueMessageTemplate); 40 | } 41 | 42 | @Test 43 | void bean_creation_test_ListeningProcessor() { 44 | assertThatSpringBean(processor); 45 | assertThat(processor.isListening()).isFalse(); 46 | } 47 | 48 | private void assertThatSpringBean(Object bean) { 49 | assertThat(bean).isNotNull(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /zola-messaging-spring-sdk/src/test/java/com/github/dhslrl321/zsmq/SharedFixture.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq; 2 | 3 | import com.github.dhslrl321.zsmq.commons.ZolaJsonSerializer; 4 | import com.github.dhslrl321.zsmq.core.message.MediaTypes; 5 | import com.github.dhslrl321.zsmq.core.message.ZolaHeader; 6 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 7 | import com.github.dhslrl321.zsmq.core.message.ZolaPayload; 8 | import com.github.dhslrl321.zsmq.core.queue.QueueName; 9 | import java.time.LocalDateTime; 10 | import lombok.AllArgsConstructor; 11 | import lombok.Data; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | public class SharedFixture { 17 | /** 18 | * this is sharedFixture that commonly use. 19 | * this is immutable and use common (any) case 20 | * 절대 변경되어서는 안됩니다 21 | */ 22 | 23 | public static final FooBean ANY_FOO_BEAN = new FooBean("jang", 26); 24 | public static final BarBean ANY_BAR_BEAN = new BarBean("seoul", 123); 25 | public static final QueueName ANY_QUEUE_NAME = QueueName.of("ANY_QUEUE_NAME"); 26 | 27 | public static final ZolaMessage ANY_JSON_MESSAGE = ZolaMessage.of(ZolaHeader.of(ANY_QUEUE_NAME, LocalDateTime.now(), 28 | MediaTypes.JSON), ZolaPayload.of(ZolaJsonSerializer.getInstance().serialize(ANY_FOO_BEAN))); 29 | 30 | private static HashMap initBeans() { 31 | HashMap map = new HashMap<>(); 32 | map.put("fooBean", ANY_FOO_BEAN); 33 | map.put("barBean", ANY_BAR_BEAN); 34 | 35 | return map; 36 | } 37 | 38 | @Data 39 | @AllArgsConstructor 40 | public static class FooBean { 41 | String name; 42 | int age; 43 | } 44 | 45 | @Data 46 | @AllArgsConstructor 47 | public static class BarBean { 48 | String address; 49 | int code; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /zola-messaging-core/src/test/java/com/github/dhslrl321/zsmq/converter/CompositeMessageConverterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.converter; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.assertj.core.api.Assertions.assertThatThrownBy; 5 | 6 | import com.github.dhslrl321.zsmq.SharedFixture; 7 | import com.github.dhslrl321.zsmq.core.message.MediaTypes; 8 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 9 | import org.junit.jupiter.api.BeforeEach; 10 | import org.junit.jupiter.api.Disabled; 11 | import org.junit.jupiter.api.Test; 12 | 13 | class CompositeMessageConverterTest { 14 | 15 | CompositeMessageConverter sut = new CompositeMessageConverter(); 16 | 17 | @BeforeEach 18 | void setUp() { 19 | sut.register(new StringMessageConverter()); 20 | sut.register(new JsonMessageConverter()); 21 | } 22 | 23 | @Test 24 | void name() { 25 | assertThatThrownBy(() -> sut.isSupport("any")).isInstanceOf(UnsupportedOperationException.class); 26 | } 27 | 28 | @Test 29 | @Disabled("not support now") 30 | void work_jsonConverter() { 31 | ZolaMessage actual = sut.toMessage("ANY_QUEUE", new SharedFixture.Foo("hello")); 32 | 33 | assertThat(actual.isSameTypeBy(MediaTypes.JSON)).isTrue(); 34 | } 35 | 36 | @Test 37 | @Disabled("not support now") 38 | void work_jsonConverter2() { 39 | String actual = sut.fromMessage(SharedFixture.ANY_JSON_MESSAGE); 40 | 41 | assertThat(actual).isEqualTo("{\"bar\":\"hello\"}"); 42 | } 43 | 44 | @Test 45 | void work_StringConverter() { 46 | ZolaMessage actual = sut.toMessage("ANY_QUEUE", "hello"); 47 | 48 | assertThat(actual.isSameTypeBy(MediaTypes.TEXT)).isTrue(); 49 | } 50 | 51 | @Test 52 | void work_StringConverter2() { 53 | String actual = sut.fromMessage(SharedFixture.ANY_STRING_MESSAGE); 54 | 55 | assertThat(actual).isEqualTo("hello"); 56 | } 57 | } -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/listener/task/ListeningTask.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.listener.task; 2 | 3 | import com.github.dhslrl321.zsmq.commons.Pair; 4 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 5 | import com.github.dhslrl321.zsmq.listener.DeletionPolicy; 6 | import com.github.dhslrl321.zsmq.listener.ListeningInformation; 7 | import com.github.dhslrl321.zsmq.listener.MessageListener; 8 | import com.github.dhslrl321.zsmq.listener.strategy.ListeningStrategy; 9 | import java.time.Duration; 10 | import java.time.temporal.ChronoUnit; 11 | import java.util.Objects; 12 | import lombok.Getter; 13 | import lombok.RequiredArgsConstructor; 14 | import org.apache.commons.lang3.ThreadUtils; 15 | 16 | @RequiredArgsConstructor 17 | public class ListeningTask implements Runnable { 18 | 19 | @Getter 20 | private final ListeningStrategy strategy; 21 | @Getter 22 | private final Pair listeningPair; 23 | 24 | @Override 25 | public void run() { 26 | boolean loop = true; 27 | while(loop) { 28 | try { 29 | ZolaMessage message = strategy.peek(listeningPair.getRight()); 30 | if (Objects.isNull(message)) { 31 | continue; 32 | } 33 | listen(message); 34 | handleAcknowledgement(); 35 | sleep(); 36 | } catch (Exception e) { 37 | loop = false; 38 | } 39 | } 40 | } 41 | 42 | private void listen(ZolaMessage message) { 43 | listeningPair.getLeft().listen(message.getPayload().getValue()); 44 | } 45 | 46 | private void sleep() throws InterruptedException { 47 | ThreadUtils.sleep(Duration.of(1, ChronoUnit.SECONDS)); 48 | } 49 | 50 | private void handleAcknowledgement() { 51 | if (listeningPair.getRight().isSameDeletionPolicy(DeletionPolicy.ALWAYS)) { 52 | strategy.acknowledgement(listeningPair.getRight()); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /integration-tests/polling-queue-prod-cons/src/test/java/com/github/dhslrl321/zsmq/integration/SimpleQueueIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.integration; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import com.github.dhslrl321.zsmq.client.ZolaClientConfig; 6 | import com.github.dhslrl321.zsmq.client.ZolaQueueMessageTemplate; 7 | import java.io.ByteArrayOutputStream; 8 | import java.io.PrintStream; 9 | import org.junit.jupiter.api.AfterEach; 10 | import org.junit.jupiter.api.BeforeEach; 11 | import org.junit.jupiter.api.Test; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.boot.test.context.SpringBootTest; 14 | 15 | @SpringBootTest 16 | public class SimpleQueueIntegrationTest { 17 | 18 | public static final String TEST_QUEUE_NAME = "TEST-QUEUE-NAME"; 19 | public static final String PUBLISH_MESSAGE = "test message publication"; 20 | /** 21 | * if you want to run this test, you should run zola-message-server in local. Later, we will provide testcontainers 22 | * for regression test 23 | */ 24 | 25 | @Autowired 26 | ZolaQueueMessageTemplate template; 27 | 28 | @Autowired 29 | ZolaClientConfig zolaClientConfig; 30 | 31 | private ByteArrayOutputStream outputStream; 32 | 33 | @BeforeEach 34 | void setUp() { 35 | outputStream = new ByteArrayOutputStream(); 36 | System.setOut(new PrintStream(outputStream)); 37 | } 38 | 39 | @AfterEach 40 | void tearDown() { 41 | System.setOut(System.out); 42 | } 43 | 44 | /** 45 | * @see BeforeTestConfiguration, There are few configurations, such as making queue or destroying queue when test is 46 | * done 47 | * 48 | * @see FooZolaMessageListener, It is simple listening setting 49 | */ 50 | @Test 51 | void producing_and_consuming() { 52 | template.convertAndSend(TEST_QUEUE_NAME, PUBLISH_MESSAGE); 53 | 54 | assertThatInConsole(PUBLISH_MESSAGE); 55 | } 56 | 57 | private void assertThatInConsole(String message) { 58 | assertThat(message).isEqualTo(outputStream.toString()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /zola-messaging-core/src/test/java/com/github/dhslrl321/zsmq/SharedFixture.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq; 2 | 3 | import com.github.dhslrl321.zsmq.commons.ZolaJsonSerializer; 4 | import com.github.dhslrl321.zsmq.core.message.MediaTypes; 5 | import com.github.dhslrl321.zsmq.core.message.ZolaHeader; 6 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 7 | import com.github.dhslrl321.zsmq.core.message.ZolaPayload; 8 | import com.github.dhslrl321.zsmq.core.queue.QueueName; 9 | import java.time.LocalDateTime; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | import lombok.AllArgsConstructor; 13 | import lombok.Data; 14 | 15 | public class SharedFixture { 16 | /** 17 | * this is sharedFixture that commonly use. 18 | * this is immutable and use common (any) case 19 | * 절대 변경되어서는 안됩니다 20 | */ 21 | 22 | public static final FooBean ANY_FOO_BEAN = new FooBean("jang", 26); 23 | public static final BarBean ANY_BAR_BEAN = new BarBean("seoul", 123); 24 | public static final Map ANY_BEANS = initBeans(); 25 | public static final QueueName ANY_QUEUE_NAME = QueueName.of("ANY_QUEUE_NAME"); 26 | public static final ZolaMessage ANY_STRING_MESSAGE = ZolaMessage.of(ZolaHeader.of(ANY_QUEUE_NAME, LocalDateTime.now(), 27 | MediaTypes.TEXT), ZolaPayload.of("hello")); 28 | 29 | public static final ZolaMessage ANY_JSON_MESSAGE = ZolaMessage.of(ZolaHeader.of(ANY_QUEUE_NAME, LocalDateTime.now(), 30 | MediaTypes.JSON), ZolaPayload.of(ZolaJsonSerializer.getInstance().serialize(new Foo("hello")))); 31 | 32 | private static HashMap initBeans() { 33 | HashMap map = new HashMap<>(); 34 | map.put("fooBean", ANY_FOO_BEAN); 35 | map.put("barBean", ANY_BAR_BEAN); 36 | 37 | return map; 38 | } 39 | 40 | @Data 41 | @AllArgsConstructor 42 | public static class Foo { 43 | String bar; 44 | } 45 | 46 | @Data 47 | @AllArgsConstructor 48 | public static class FooBean { 49 | String name; 50 | int age; 51 | } 52 | 53 | @Data 54 | @AllArgsConstructor 55 | public static class BarBean { 56 | String address; 57 | int code; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /zola-messaging-core/src/test/java/com/github/dhslrl321/zsmq/core/queue/ZolaSimpleQueueTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.core.queue; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.assertj.core.api.Assertions.assertThatThrownBy; 5 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 6 | 7 | import com.github.dhslrl321.zsmq.exception.EmptyQueueException; 8 | import com.github.dhslrl321.zsmq.core.message.ZolaHeader; 9 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 10 | import com.github.dhslrl321.zsmq.core.message.ZolaPayload; 11 | import java.time.LocalDateTime; 12 | import org.junit.jupiter.api.Test; 13 | 14 | class ZolaSimpleQueueTest { 15 | 16 | static final QueueName ANY_NAME = QueueName.of("ANY_NAME"); 17 | static final ZolaPayload ANY_ZOLA_PAYLOAD = ZolaPayload.of(""); 18 | ZolaSimpleQueue sut = ZolaSimpleQueue.newInstance(ANY_NAME); 19 | 20 | ZolaMessage msg1 = ZolaMessage.of(ZolaHeader.of(ANY_NAME, LocalDateTime.now(), null), ANY_ZOLA_PAYLOAD); 21 | ZolaMessage msg2 = ZolaMessage.of(ZolaHeader.of(ANY_NAME, LocalDateTime.now(), null), ANY_ZOLA_PAYLOAD); 22 | 23 | @Test 24 | void added_when_push() { 25 | sut.push(msg1); 26 | assertThat(sut.size()).isEqualTo(1); 27 | 28 | sut.push(msg2); 29 | assertThat(sut.size()).isEqualTo(2); 30 | } 31 | 32 | @Test 33 | void peek_with_order() { 34 | initSUTWithOrder(msg1, msg2); 35 | 36 | ZolaMessage actual = sut.peek(); 37 | 38 | assertThat(actual).isEqualTo(msg1); 39 | } 40 | 41 | @Test 42 | void pop_always_return_last_added() { 43 | 44 | initSUTWithOrder(msg1, msg2); 45 | 46 | ZolaMessage actual = sut.pop(); 47 | assertThat(actual).isEqualTo(msg1); 48 | 49 | ZolaMessage actual2 = sut.pop(); 50 | assertThat(actual2).isEqualTo(msg2); 51 | } 52 | 53 | @Test 54 | void if_empty_peek_return_null() { 55 | ZolaMessage actual = sut.peek(); 56 | assertThat(actual).isNull(); 57 | } 58 | 59 | @Test 60 | void if_empty_pop_throw_exception() { 61 | assertThatThrownBy(() -> sut.pop()).isInstanceOf(EmptyQueueException.class); 62 | } 63 | 64 | private void initSUTWithOrder(ZolaMessage msg1, ZolaMessage msg2) { 65 | sut.push(msg1); 66 | sut.push(msg2); 67 | } 68 | } -------------------------------------------------------------------------------- /zola-messaging-core/src/test/java/com/github/dhslrl321/zsmq/client/ZolaQueueMessageTemplateTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.client; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.assertj.core.api.Assertions.assertThatThrownBy; 5 | import static org.mockito.BDDMockito.given; 6 | import static org.mockito.Mockito.mock; 7 | import static org.mockito.Mockito.verify; 8 | 9 | import com.github.dhslrl321.zsmq.client.ZolaClientConfig; 10 | import com.github.dhslrl321.zsmq.client.ZolaQueueMessageTemplate; 11 | import com.github.dhslrl321.zsmq.client.spy.SpyZolaHttpClient; 12 | import com.github.dhslrl321.zsmq.http.ZolaHttpClient; 13 | import com.github.dhslrl321.zsmq.core.message.MediaTypes; 14 | import com.github.dhslrl321.zsmq.core.message.ZolaHeader; 15 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 16 | import com.github.dhslrl321.zsmq.core.message.ZolaPayload; 17 | import com.github.dhslrl321.zsmq.core.queue.QueueName; 18 | import java.time.LocalDateTime; 19 | import org.junit.jupiter.api.BeforeEach; 20 | import org.junit.jupiter.api.Disabled; 21 | import org.junit.jupiter.api.Test; 22 | 23 | class ZolaQueueMessageTemplateTest { 24 | 25 | public static final String ANY_STRING = "ANY_STRING"; 26 | 27 | ZolaQueueMessageTemplate sut; 28 | 29 | SpyZolaHttpClient spyHttpClient = new SpyZolaHttpClient(); 30 | 31 | @BeforeEach 32 | void setUp() { 33 | sut = new ZolaQueueMessageTemplate(new ZolaClientConfig(ANY_STRING), spyHttpClient); 34 | } 35 | 36 | @Test 37 | void happy_case() { 38 | ZolaMessage message = ZolaMessage.of(ZolaHeader.of(QueueName.of(ANY_STRING), LocalDateTime.now(), MediaTypes.JSON), 39 | ZolaPayload.of(ANY_STRING)); 40 | 41 | sut.convertAndSend(ANY_STRING, message); 42 | 43 | assertThat(spyHttpClient.pushCalled).isTrue(); 44 | } 45 | 46 | @Test 47 | void queueName_must_be_notEmpty() { 48 | assertThatThrownBy(() -> sut.convertAndSend("", ANY_STRING)) 49 | .isInstanceOf(IllegalArgumentException.class); 50 | } 51 | 52 | @Test 53 | void queueName_must_be_notNull() { 54 | assertThatThrownBy(() -> sut.convertAndSend(null, ANY_STRING)) 55 | .isInstanceOf(IllegalArgumentException.class); 56 | } 57 | 58 | @Test 59 | void payload_must_be_notNull() { 60 | assertThatThrownBy(() -> sut.convertAndSend(ANY_STRING, null)) 61 | .isInstanceOf(IllegalArgumentException.class); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /zola-messaging-spring-sdk/src/test/java/com/github/dhslrl321/zsmq/listener/strategy/HttpPollingListeningStrategyTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.listener.strategy; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.mockito.BDDMockito.given; 5 | import static org.mockito.Mockito.mock; 6 | 7 | import com.github.dhslrl321.zsmq.SharedFixture; 8 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 9 | import com.github.dhslrl321.zsmq.http.ZolaHttpClient; 10 | import com.github.dhslrl321.zsmq.listener.DeletionPolicy; 11 | import com.github.dhslrl321.zsmq.listener.ListeningInformation; 12 | import java.util.Optional; 13 | import org.junit.jupiter.api.BeforeEach; 14 | import org.junit.jupiter.api.Test; 15 | 16 | class HttpPollingListeningStrategyTest { 17 | 18 | public static final ListeningInformation LISTENING_INFORMATION = ListeningInformation.of("ANY_SERVER", 19 | SharedFixture.ANY_QUEUE_NAME.getValue(), DeletionPolicy.ALWAYS); 20 | HttpPollingListeningStrategy sut; 21 | 22 | ZolaHttpClient mockHttpClient = mock(ZolaHttpClient.class); 23 | 24 | @BeforeEach 25 | void setUp() { 26 | sut = new HttpPollingListeningStrategy(); 27 | sut.setHttpClient(mockHttpClient); 28 | } 29 | 30 | @Test 31 | void peek_not_empty() { 32 | given(mockHttpClient.requestPeek("ANY_SERVER", SharedFixture.ANY_QUEUE_NAME.getValue())) 33 | .willReturn(Optional.of(SharedFixture.ANY_JSON_MESSAGE)); 34 | 35 | ZolaMessage actual = sut.peek(LISTENING_INFORMATION); 36 | 37 | assertThat(actual).isNotNull(); 38 | } 39 | 40 | @Test 41 | void peek_empty() { 42 | given(mockHttpClient.requestPeek("ANY_SERVER", SharedFixture.ANY_QUEUE_NAME.getValue())) 43 | .willReturn(Optional.empty()); 44 | 45 | ZolaMessage actual = sut.peek(LISTENING_INFORMATION); 46 | 47 | assertThat(actual).isNull(); 48 | } 49 | 50 | @Test 51 | void ack_true() { 52 | given(mockHttpClient.acknowledgement("ANY_SERVER", SharedFixture.ANY_QUEUE_NAME.getValue())).willReturn(true); 53 | 54 | boolean actual = sut.acknowledgement(LISTENING_INFORMATION); 55 | 56 | assertThat(actual).isTrue(); 57 | } 58 | 59 | @Test 60 | void ack_false() { 61 | given(mockHttpClient.acknowledgement("ANY_SERVER", SharedFixture.ANY_QUEUE_NAME.getValue())).willReturn(false); 62 | 63 | boolean actual = sut.acknowledgement(LISTENING_INFORMATION); 64 | 65 | assertThat(actual).isFalse(); 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/commons/ZolaJsonSerializer.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.commons; 2 | 3 | import com.github.dhslrl321.zsmq.exception.ZolaSerializeException; 4 | import com.google.gson.Gson; 5 | import com.google.gson.GsonBuilder; 6 | import com.google.gson.JsonDeserializationContext; 7 | import com.google.gson.JsonDeserializer; 8 | import com.google.gson.JsonElement; 9 | import com.google.gson.JsonParseException; 10 | import com.google.gson.JsonPrimitive; 11 | import com.google.gson.JsonSerializationContext; 12 | import com.google.gson.JsonSerializer; 13 | import java.lang.reflect.Type; 14 | import java.time.LocalDateTime; 15 | import java.time.format.DateTimeFormatter; 16 | 17 | public class ZolaJsonSerializer { 18 | // TODO need memory optimization 19 | private final Gson gson; 20 | 21 | private ZolaJsonSerializer() { 22 | gson = new GsonBuilder() 23 | .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeSerializer()) 24 | .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeDeserializer()) 25 | .serializeNulls() 26 | .create(); 27 | } 28 | 29 | private static class SingletonHolder { 30 | private static final ZolaJsonSerializer instance = new ZolaJsonSerializer(); 31 | } 32 | 33 | public static ZolaJsonSerializer getInstance() { 34 | return SingletonHolder.instance; 35 | } 36 | 37 | public String serialize(Object object) { 38 | try { 39 | return gson.toJson(object); 40 | } catch (Exception e) { 41 | throw new ZolaSerializeException(e.getMessage()); 42 | } 43 | } 44 | 45 | public T deserialize(String json, Class clazz) { 46 | try { 47 | return gson.fromJson(json, clazz); 48 | } catch (Exception e) { 49 | throw new ZolaSerializeException(e.getMessage()); 50 | } 51 | } 52 | 53 | private static class LocalDateTimeSerializer implements JsonSerializer { 54 | @Override 55 | public JsonElement serialize(LocalDateTime src, Type typeOfSrc, JsonSerializationContext context) { 56 | return new JsonPrimitive(src.format(DateTimeFormatter.ISO_DATE_TIME)); 57 | } 58 | } 59 | 60 | private static class LocalDateTimeDeserializer implements JsonDeserializer { 61 | @Override 62 | public LocalDateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) 63 | throws JsonParseException { 64 | return LocalDateTime.parse(json.getAsJsonPrimitive().getAsString(), DateTimeFormatter.ISO_DATE_TIME); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/core/queue/ZolaQueueContainer.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.core.queue; 2 | 3 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 4 | import com.github.dhslrl321.zsmq.exception.QueueNotFoundException; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.NoSuchElementException; 9 | import java.util.stream.Collectors; 10 | 11 | public class ZolaQueueContainer { 12 | 13 | private final Map registered = new HashMap<>(); 14 | 15 | public void register(ZolaQueue queue) { 16 | throwWhenDuplicated(queue); 17 | registered.put(queue.getName(), queue); 18 | } 19 | 20 | public QueueDescribe describe() { 21 | List info = registered.values().stream().map(this::getInfoBy).collect(Collectors.toList()); 22 | return QueueDescribe.of(registered.size(), info); 23 | } 24 | 25 | protected int size() { 26 | return registered.size(); 27 | } 28 | 29 | public ZolaMessage peekBy(QueueName queueName) { 30 | throwWhenNotFound(queueName); 31 | return get(queueName).peek(); 32 | } 33 | 34 | public void pushBy(ZolaMessage zolaMessage) { 35 | QueueName queueName = zolaMessage.getHeader().getQueueName(); 36 | throwWhenNotFound(queueName); 37 | get(queueName).push(zolaMessage); 38 | } 39 | 40 | public void popBy(QueueName queueName) { 41 | throwWhenNotFound(queueName); 42 | get(queueName).pop(); 43 | } 44 | 45 | public void removeBy(String queueName) { 46 | throwWhenNotFound(QueueName.of(queueName)); 47 | registered.remove(QueueName.of(queueName)); 48 | } 49 | 50 | public boolean contains(String queueName) { 51 | return registered.containsKey(QueueName.of(queueName)); 52 | } 53 | 54 | protected ZolaQueue get(QueueName queueName) { 55 | throwWhenNotFound(queueName); 56 | return registered.get(queueName); 57 | } 58 | 59 | private void throwWhenNotFound(QueueName queueName) { 60 | if (!registered.containsKey(queueName)) { 61 | throw new QueueNotFoundException(); 62 | } 63 | } 64 | 65 | private void throwWhenDuplicated(ZolaQueue queue) { 66 | if (registered.containsKey(queue.getName())) { 67 | String message = String.format("A queue named [%s] already exists. ", queue.getName().getValue()); 68 | throw new IllegalArgumentException(message); 69 | } 70 | } 71 | 72 | private QueueInfo getInfoBy(ZolaQueue zolaQueue) { 73 | return QueueInfo.of(zolaQueue.getName().getValue(), zolaQueue.size(), zolaQueue.getCreatedAt()); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /zola-messaging-spring-sdk/src/main/java/com/github/dhslrl321/zsmq/detector/SpringBeanMessageListenerDetector.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.detector; 2 | 3 | import com.github.dhslrl321.zsmq.annotation.ZolaMessageListener; 4 | import com.github.dhslrl321.zsmq.client.ZolaClientConfig; 5 | import com.github.dhslrl321.zsmq.commons.Pair; 6 | import com.github.dhslrl321.zsmq.listener.InvalidUseOfZolaMessageListenerException; 7 | import com.github.dhslrl321.zsmq.listener.ListeningInformation; 8 | import com.github.dhslrl321.zsmq.listener.MessageListener; 9 | import com.github.dhslrl321.zsmq.listener.SpringBeanMessageListener; 10 | import java.lang.reflect.Method; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.Map; 14 | import lombok.RequiredArgsConstructor; 15 | import lombok.SneakyThrows; 16 | 17 | @RequiredArgsConstructor 18 | public class SpringBeanMessageListenerDetector implements MessageListenerDetector { 19 | 20 | private final Map beans; 21 | private final ZolaClientConfig config; 22 | 23 | @SneakyThrows 24 | @Override 25 | public List> detect() { 26 | final List> pairs = new ArrayList<>(); 27 | for (Object o : beans.values()) { 28 | Class aClass = o.getClass(); 29 | Method[] methods = aClass.getMethods(); 30 | for (Method method : methods) { 31 | if (method.isAnnotationPresent(ZolaMessageListener.class)) { 32 | ZolaMessageListener annotation = method.getAnnotation(ZolaMessageListener.class); 33 | validateListenerAnnotation(method, annotation); 34 | 35 | Class[] parameterTypes = method.getParameterTypes(); 36 | throwIfNotSupportParamType(parameterTypes); 37 | 38 | pairs.add(Pair.of(SpringBeanMessageListener.of(o, method), 39 | ListeningInformation.of(config.getServerBaseUrl(), annotation.queueName(), annotation.deletionPolicy()))); 40 | } 41 | } 42 | } 43 | return pairs; 44 | } 45 | 46 | private void throwIfNotSupportParamType(Class[] parameterTypes) { 47 | for (Class parameterType : parameterTypes) { 48 | if (!parameterType.equals(String.class)) { 49 | throw new InvalidUseOfZolaMessageListenerException( 50 | "listener method's parameter have to be String!"); 51 | } 52 | } 53 | } 54 | 55 | private void validateListenerAnnotation(Method method, ZolaMessageListener annotation) { 56 | if (annotation.queueName().isBlank() || method.getParameters().length == 0) { 57 | throw new InvalidUseOfZolaMessageListenerException("queueName should be not blank"); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /zola-messaging-core/src/test/java/com/github/dhslrl321/zsmq/core/queue/ZolaQueueContainerTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.core.queue; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.assertj.core.api.Assertions.assertThatThrownBy; 5 | 6 | import com.github.dhslrl321.zsmq.core.message.ZolaHeader; 7 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 8 | import com.github.dhslrl321.zsmq.core.message.ZolaPayload; 9 | import com.github.dhslrl321.zsmq.exception.QueueNotFoundException; 10 | import java.time.LocalDateTime; 11 | import java.util.NoSuchElementException; 12 | import org.junit.jupiter.api.BeforeEach; 13 | import org.junit.jupiter.api.Test; 14 | 15 | class ZolaQueueContainerTest { 16 | 17 | public static final QueueName FOO_QUEUE_NAME = QueueName.of("foo"); 18 | public static final ZolaSimpleQueue FOO_ZOLA_QUEUE = ZolaSimpleQueue.newInstance(FOO_QUEUE_NAME); 19 | 20 | ZolaQueueContainer sut; 21 | 22 | @BeforeEach 23 | void setUp() { 24 | sut = new ZolaQueueContainer(); 25 | } 26 | 27 | @Test 28 | void name() { 29 | QueueDescribe actual = sut.describe(); 30 | 31 | assertThat(actual).isNotNull(); 32 | } 33 | 34 | @Test 35 | void can_register() { 36 | sut.register(FOO_ZOLA_QUEUE); 37 | assertThat(sut.size()).isEqualTo(1); 38 | 39 | sut.register(ZolaSimpleQueue.newInstance(QueueName.of("bar"))); 40 | assertThat(sut.size()).isEqualTo(2); 41 | } 42 | 43 | @Test 44 | void unique_by_name() { 45 | sut.register(FOO_ZOLA_QUEUE); 46 | assertThat(sut.size()).isEqualTo(1); 47 | 48 | assertThatThrownBy(() -> sut.register(FOO_ZOLA_QUEUE)) 49 | .isInstanceOf(IllegalArgumentException.class); 50 | } 51 | 52 | @Test 53 | void peek_by_name() { 54 | ZolaMessage zolaMessage = ZolaMessage.of(ZolaHeader.of(QueueName.of(""), LocalDateTime.now(), null), ZolaPayload.of("hello")); 55 | FOO_ZOLA_QUEUE.push(zolaMessage); 56 | sut.register(FOO_ZOLA_QUEUE); 57 | 58 | ZolaMessage actual = sut.peekBy(FOO_QUEUE_NAME); 59 | 60 | assertThat(actual.getPayload()).isEqualTo(ZolaPayload.of("hello")); 61 | } 62 | 63 | @Test 64 | void throw_when_not_exist() { 65 | assertThatThrownBy(() -> sut.peekBy(FOO_QUEUE_NAME)) 66 | .isInstanceOf(QueueNotFoundException.class); 67 | } 68 | 69 | @Test 70 | void isContain_by_name() { 71 | sut.register(ZolaSimpleQueue.newInstance(QueueName.of("Q_NAME"))); 72 | 73 | boolean actual = sut.contains("Q_NAME"); 74 | 75 | assertThat(actual).isTrue(); 76 | } 77 | 78 | @Test 79 | void isContain_by_name_not_exist() { 80 | boolean actual = sut.contains("Q_NAME"); 81 | 82 | assertThat(actual).isFalse(); 83 | } 84 | 85 | @Test 86 | void remove() { 87 | sut.register(ZolaSimpleQueue.newInstance(QueueName.of("Q-NAME"))); 88 | assertThat(sut.size()).isEqualTo(1); 89 | 90 | sut.removeBy("Q-NAME"); 91 | assertThat(sut.size()).isEqualTo(0); 92 | } 93 | 94 | @Test 95 | void remove_fail() { 96 | assertThatThrownBy(() -> sut.removeBy("Q-NAME")) 97 | .isInstanceOf(QueueNotFoundException.class); 98 | } 99 | } -------------------------------------------------------------------------------- /zola-messaging-spring-boot-autoconfigure/src/main/java/com/github/dhslrl321/zsmq/ZsmqAutoconfiguration.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq; 2 | 3 | import com.github.dhslrl321.zsmq.annotation.ZolaConsumer; 4 | import com.github.dhslrl321.zsmq.client.ZolaClientConfig; 5 | import com.github.dhslrl321.zsmq.client.ZolaQueueMessageTemplate; 6 | import com.github.dhslrl321.zsmq.detector.MessageListenerDetector; 7 | import com.github.dhslrl321.zsmq.detector.SpringBeanMessageListenerDetector; 8 | import com.github.dhslrl321.zsmq.http.ZolaHttpClient; 9 | import com.github.dhslrl321.zsmq.listener.ZolaMessageListeningProcessor; 10 | import com.github.dhslrl321.zsmq.listener.task.ListeningTaskExecutor; 11 | import com.github.dhslrl321.zsmq.listener.task.PollingQueueListeningTaskFactory; 12 | import com.github.dhslrl321.zsmq.listener.task.ThreadPoolListeningExecutor; 13 | import lombok.RequiredArgsConstructor; 14 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 15 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 16 | import org.springframework.context.ApplicationContext; 17 | import org.springframework.context.annotation.Bean; 18 | import org.springframework.context.annotation.Configuration; 19 | 20 | @Configuration 21 | @RequiredArgsConstructor 22 | @EnableConfigurationProperties 23 | public class ZsmqAutoconfiguration { 24 | private final ZsmqProperty property; 25 | 26 | @Bean 27 | @ConditionalOnMissingBean 28 | public ZolaClientConfig zolaClientConfig() { 29 | return new ZolaClientConfig(property.getUrl()); 30 | } 31 | 32 | @Bean 33 | @ConditionalOnMissingBean 34 | public ZolaQueueMessageTemplate zolaQueueMessageTemplate(ZolaClientConfig zolaClientConfig) { 35 | return new ZolaQueueMessageTemplate(zolaClientConfig, new ZolaHttpClient()); 36 | } 37 | 38 | @Bean 39 | @ConditionalOnMissingBean 40 | public ZolaMessageListeningProcessor container(ApplicationContext applicationContext, 41 | ZolaClientConfig zolaClientConfig) { 42 | ZolaMessageListeningProcessor zolaMessageListeningProcessor = newProcessor( 43 | newDetector(applicationContext, zolaClientConfig), 44 | newExecutor(), 45 | newTaskFactory() 46 | ); 47 | doProcessIfListening(zolaMessageListeningProcessor); 48 | return zolaMessageListeningProcessor; 49 | } 50 | 51 | private void doProcessIfListening(ZolaMessageListeningProcessor zolaMessageListeningProcessor) { 52 | if (property.isListening()) { 53 | zolaMessageListeningProcessor.doProcess(); 54 | } 55 | } 56 | 57 | private ZolaMessageListeningProcessor newProcessor(MessageListenerDetector detector, 58 | ListeningTaskExecutor taskExecutor, 59 | PollingQueueListeningTaskFactory taskFactory) { 60 | return new ZolaMessageListeningProcessor(detector, 61 | taskFactory, taskExecutor); 62 | } 63 | 64 | private PollingQueueListeningTaskFactory newTaskFactory() { 65 | return new PollingQueueListeningTaskFactory(); 66 | } 67 | 68 | private ThreadPoolListeningExecutor newExecutor() { 69 | return new ThreadPoolListeningExecutor(); 70 | } 71 | 72 | private SpringBeanMessageListenerDetector newDetector(ApplicationContext applicationContext, 73 | ZolaClientConfig zolaClientConfig) { 74 | return new SpringBeanMessageListenerDetector( 75 | applicationContext.getBeansWithAnnotation(ZolaConsumer.class), zolaClientConfig); 76 | } 77 | } 78 | 79 | -------------------------------------------------------------------------------- /zola-messaging-core/src/main/java/com/github/dhslrl321/zsmq/http/ZolaHttpClient.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.http; 2 | 3 | import com.github.dhslrl321.zsmq.commons.ZolaJsonSerializer; 4 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 5 | import java.util.Objects; 6 | import java.util.Optional; 7 | import okhttp3.Call; 8 | import okhttp3.MediaType; 9 | import okhttp3.OkHttpClient; 10 | import okhttp3.Request; 11 | import okhttp3.Request.Builder; 12 | import okhttp3.RequestBody; 13 | import okhttp3.Response; 14 | 15 | public class ZolaHttpClient { 16 | private static final MediaType JSON = MediaType.get("application/json; charset=utf-8"); 17 | private static final int NO_CONTENT = 204; 18 | private static final int NOT_FOUND = 404; 19 | private static final int BAD_REQUEST = 400; 20 | 21 | private final OkHttpClient http = new OkHttpClient(); 22 | 23 | public boolean requestPush(String baseUrl, ZolaMessage message) { 24 | Request request = new Builder() 25 | .url(baseUrl + "/api/messages") 26 | .post(RequestBody.create(ZolaJsonSerializer.getInstance().serialize(message), JSON)) 27 | .build(); 28 | Call call = http.newCall(request); 29 | try (Response response = call.execute()){ 30 | validateResponse(response.code(), message.getQueueNameValue()); 31 | return true; 32 | } catch (Exception e) { 33 | e.printStackTrace(); 34 | throw new ZolaServerConnectionFailedException( 35 | "unexpected exception occurred while communicate with zola messaging server"); 36 | } 37 | } 38 | 39 | public Optional requestPeek(String baseUrl, String queueName) { 40 | Request request = new Builder() 41 | .url(baseUrl + "/api/queues/" + queueName) 42 | .get() 43 | .build(); 44 | Call call = http.newCall(request); 45 | try (Response response = call.execute()){ 46 | validateResponse(response.code(), queueName); 47 | if (NO_CONTENT == response.code()) { 48 | return Optional.empty(); 49 | } 50 | return Optional.of( 51 | ZolaJsonSerializer.getInstance().deserialize(Objects.requireNonNull(response.body()).string(), ZolaMessage.class)); 52 | } catch (Exception e) { 53 | e.printStackTrace(); 54 | throw new ZolaServerConnectionFailedException( 55 | "unexpected exception occurred while communicate with zola messaging server"); 56 | } 57 | } 58 | 59 | public boolean acknowledgement(String baseUrl, String queueName) { 60 | Request request = new Builder() 61 | .url(baseUrl + "/api/queues/" + queueName + "/acknowledge") 62 | .delete() 63 | .build(); 64 | Call call = http.newCall(request); 65 | try (Response response = call.execute()){ 66 | validateResponse(response.code(), queueName); 67 | return true; 68 | } catch (Exception e) { 69 | e.printStackTrace(); 70 | throw new ZolaServerConnectionFailedException( 71 | "unexpected exception occurred while communicate with zola messaging server"); 72 | } 73 | } 74 | 75 | private void validateResponse(int code, String message) { 76 | badRequest(code); 77 | notFound(code, message); 78 | } 79 | 80 | private void notFound(int code, String queueName) { 81 | if (NOT_FOUND == code) { 82 | throw new ZolaServerCommunicationException( 83 | String.format("zola messaging server push failed! Queue Name [%s] was not found!", queueName)); 84 | } 85 | } 86 | 87 | private void badRequest(int code) { 88 | if (BAD_REQUEST == code) { 89 | throw new ZolaServerCommunicationException("zola messaging server push failed! with invalid request body data"); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /zola-messaging-server/src/main/java/com/github/dhslrl321/zsmq/controller/CommandQueueController.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.controller; 2 | 3 | import com.github.dhslrl321.zsmq.controller.model.MessageModel; 4 | import com.github.dhslrl321.zsmq.controller.model.ModelConverter; 5 | import com.github.dhslrl321.zsmq.controller.model.SimpleResponse; 6 | import com.github.dhslrl321.zsmq.core.message.ZolaMessage; 7 | import com.github.dhslrl321.zsmq.core.queue.QueueName; 8 | import com.github.dhslrl321.zsmq.core.queue.ZolaQueueContainer; 9 | import com.github.dhslrl321.zsmq.core.queue.ZolaSimpleQueue; 10 | import java.util.Map; 11 | import lombok.RequiredArgsConstructor; 12 | import org.apache.commons.lang3.StringUtils; 13 | import org.springframework.http.HttpStatus; 14 | import org.springframework.http.ResponseEntity; 15 | import org.springframework.web.bind.annotation.CrossOrigin; 16 | import org.springframework.web.bind.annotation.DeleteMapping; 17 | import org.springframework.web.bind.annotation.PathVariable; 18 | import org.springframework.web.bind.annotation.PostMapping; 19 | import org.springframework.web.bind.annotation.RequestBody; 20 | import org.springframework.web.bind.annotation.RequestMapping; 21 | import org.springframework.web.bind.annotation.RequestMethod; 22 | import org.springframework.web.bind.annotation.RestController; 23 | 24 | @RestController 25 | @RequestMapping(value = "/api") 26 | @RequiredArgsConstructor 27 | @CrossOrigin(value = "*", methods = { 28 | RequestMethod.GET, 29 | RequestMethod.POST, 30 | RequestMethod.PATCH, 31 | RequestMethod.PUT, 32 | RequestMethod.DELETE, 33 | RequestMethod.OPTIONS}) 34 | public class CommandQueueController { 35 | 36 | private final ZolaQueueContainer container; 37 | 38 | @PostMapping("/queues") 39 | public ResponseEntity createQ(@RequestBody Map requestBody) { 40 | String name = requestBody.get("name"); 41 | if (isBlankOrLengthOver40(name)) { 42 | return badRequest(); 43 | } 44 | container.register(getNewZolaQueue(name)); 45 | 46 | return created("create success"); 47 | } 48 | 49 | @DeleteMapping("/queues/{name}") 50 | public ResponseEntity deleteQ(@PathVariable String name) { 51 | if (isBlankOrLengthOver40(name)) { 52 | return badRequest(); 53 | } 54 | if (!container.contains(name)) { 55 | return notFound(); 56 | } 57 | container.removeBy(name); 58 | return noContent("remove success"); 59 | } 60 | 61 | @PostMapping("/messages") 62 | public ResponseEntity push(@RequestBody MessageModel request) { 63 | ZolaMessage zolaMessage = ModelConverter.convert(request); 64 | container.pushBy(zolaMessage); 65 | return created("message add success"); 66 | } 67 | 68 | @DeleteMapping("/queues/{name}/acknowledge") 69 | public ResponseEntity ack(@PathVariable String name) { 70 | container.popBy(QueueName.of(name)); 71 | return noContent("ack success"); 72 | } 73 | 74 | private ZolaSimpleQueue getNewZolaQueue(String name) { 75 | return ZolaSimpleQueue.newInstance(QueueName.of(name)); 76 | } 77 | 78 | private boolean isBlankOrLengthOver40(String name) { 79 | return StringUtils.isBlank(name) || name.length() > 40; 80 | } 81 | 82 | private ResponseEntity badRequest() { 83 | return ResponseEntity.status(HttpStatus.BAD_REQUEST).build(); 84 | } 85 | 86 | private ResponseEntity created(String message) { 87 | return ResponseEntity.status(HttpStatus.CREATED).body(new SimpleResponse(message)); 88 | } 89 | 90 | private ResponseEntity notFound() { 91 | return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); 92 | } 93 | 94 | private ResponseEntity noContent(String message) { 95 | return ResponseEntity.status(HttpStatus.NO_CONTENT).body(new SimpleResponse(message)); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /zola-messaging-spring-sdk/src/test/java/com/github/dhslrl321/zsmq/detector/SpringBeanMessageListenerDetectorTest.java: -------------------------------------------------------------------------------- 1 | package com.github.dhslrl321.zsmq.detector; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.assertj.core.api.Assertions.assertThatThrownBy; 5 | import static org.mockito.BDDMockito.given; 6 | import static org.mockito.Mockito.mock; 7 | 8 | import com.github.dhslrl321.zsmq.annotation.ZolaConsumer; 9 | import com.github.dhslrl321.zsmq.annotation.ZolaMessageListener; 10 | import com.github.dhslrl321.zsmq.client.ZolaClientConfig; 11 | import com.github.dhslrl321.zsmq.commons.Pair; 12 | import com.github.dhslrl321.zsmq.listener.DeletionPolicy; 13 | import com.github.dhslrl321.zsmq.listener.InvalidUseOfZolaMessageListenerException; 14 | import com.github.dhslrl321.zsmq.listener.ListeningInformation; 15 | import com.github.dhslrl321.zsmq.listener.MessageListener; 16 | import com.github.dhslrl321.zsmq.listener.SpringBeanMessageListener; 17 | import java.lang.reflect.Method; 18 | import java.util.List; 19 | import java.util.Map; 20 | import lombok.EqualsAndHashCode; 21 | import org.junit.jupiter.api.BeforeEach; 22 | import org.junit.jupiter.api.Test; 23 | 24 | class SpringBeanMessageListenerDetectorTest { 25 | public static final String ANY_SERVER = "any_server"; 26 | public static final String ANY_QUEUE_NAME = "ANY_QUEUE_NAME"; 27 | 28 | SpringBeanMessageListenerDetector sut; 29 | 30 | ZolaClientConfig config = mock(ZolaClientConfig.class); 31 | 32 | @EqualsAndHashCode 33 | @ZolaConsumer 34 | static class Foo { 35 | @ZolaMessageListener(queueName = "ANY_QUEUE_NAME", deletionPolicy = DeletionPolicy.ALWAYS) 36 | public void fooMethod(String message) { 37 | } 38 | } 39 | 40 | @Test 41 | void happy_case() throws Exception { 42 | Foo fooClass = new Foo(); 43 | Method barMethod = fooClass.getClass().getMethod("fooMethod", String.class); 44 | Pair pair = Pair.of(SpringBeanMessageListener.of(fooClass, barMethod), 45 | ListeningInformation.of(ANY_SERVER, ANY_QUEUE_NAME, DeletionPolicy.ALWAYS)); 46 | 47 | sut = new SpringBeanMessageListenerDetector(Map.of("someClass", new Foo()), config); 48 | given(config.getServerBaseUrl()).willReturn(ANY_SERVER); 49 | 50 | List> actual = sut.detect(); 51 | 52 | assertThat(actual.get(0)).isEqualTo(pair); 53 | } 54 | 55 | 56 | @ZolaConsumer 57 | static class Bar { 58 | public void barMethod(String message) { 59 | } 60 | } 61 | 62 | @Test 63 | void not_return_pair_when_no_zolaMessageListener_annotation() { 64 | sut = new SpringBeanMessageListenerDetector(Map.of("someClass", new Bar()), config); 65 | List> actual = sut.detect(); 66 | 67 | assertThat(actual.size()).isZero(); 68 | } 69 | 70 | 71 | @ZolaConsumer 72 | static class Baz { 73 | @ZolaMessageListener(queueName = "", deletionPolicy = DeletionPolicy.ALWAYS) 74 | public void bazMethod(String message) { 75 | } 76 | } 77 | 78 | @Test 79 | void throw_when_empty_queueName() { 80 | sut = new SpringBeanMessageListenerDetector(Map.of("someClass", new Baz()), config); 81 | assertThatThrownBy(() -> sut.detect()) 82 | .isInstanceOf(InvalidUseOfZolaMessageListenerException.class); 83 | } 84 | 85 | 86 | @ZolaConsumer 87 | static class Qux { 88 | @ZolaMessageListener(queueName = "ANY_QUEUE_NAME", deletionPolicy = DeletionPolicy.ALWAYS) 89 | public void quxMethod() { 90 | } 91 | } 92 | 93 | @Test 94 | void throw_when_missing_parameter() { 95 | sut = new SpringBeanMessageListenerDetector(Map.of("someClass", new Qux()), config); 96 | assertThatThrownBy(() -> sut.detect()) 97 | .isInstanceOf(InvalidUseOfZolaMessageListenerException.class); 98 | } 99 | 100 | 101 | @ZolaConsumer 102 | static class Qux2 { 103 | @ZolaMessageListener(queueName = "ANY_QUEUE_NAME", deletionPolicy = DeletionPolicy.ALWAYS) 104 | public void qux2Method(int message) { 105 | } 106 | } 107 | 108 | @Test 109 | void throw_when_not_string_parameter() { 110 | sut = new SpringBeanMessageListenerDetector(Map.of("someClass", new Qux2()), config); 111 | assertThatThrownBy(() -> sut.detect()) 112 | .isInstanceOf(InvalidUseOfZolaMessageListenerException.class); 113 | } 114 | 115 | } -------------------------------------------------------------------------------- /README-kor.md: -------------------------------------------------------------------------------- 1 | ### 당신이 경험한 Message Queue 중에 가장 단순한 Message Queue 2 | 3 |
4 | 5 | #### [home (engish version)](https://github.com/dhslrl321/zsmq) 6 | 7 | #### [korean version docs](https://github.com/dhslrl321/zsmq/blob/main/README-kor.md) 8 | 9 | 📗Getting Started || Overview || Docs || Change Log 10 | 11 |
12 | 13 | # ZSMQ 14 | 15 | ZSMQ 는 java 로 개발된 **졸라 간단한 메시지 큐, Zola Simple Message Queue** 입니다. 16 | 17 | > 'zola' is a korean slang. It means 'utterly', 'extremely', 'super', 'very' 18 | 19 | ZSMQ 는 운영 환경이 아닌 다양한 곳 (PoC 혹은 특정 분야의 학습) 에서 사용될 수 있습니다. 20 | 21 | 성능은 중요하지 않지만 아주 간단한 메시지 큐가 필요할 때 zsmq 는 최고의 선택입니다. 22 | 23 | ### ZSMQ 의 목표는 다음과 같습니다. 24 | 25 | - **단순함** 26 | - **쉬움** 27 | - **직관적인 설정 및 사용** 28 | - **당신이 message queue 이외에 것들에 집중하는 것을** 29 | 30 | # Quick Start 31 | 32 | ZSMQ 는 빠르고 쉽게 사용할 수 있는 시스템이기 때문에 최소한의 설정을 목표로 합니다. 33 | 34 | > 자세한 설정들을 확인하시려면 [example](https://github.com/dhslrl321/zsmq-example) 을 확인하세요. zsmq 를 이용한 간단한 예제가 존재합니다. 35 | 36 | 아래의 4가지 단계만 거치면 쉽게 메시지 큐 서버 하나를 사용할 수 있습니다. 37 | 38 | 1. messaging server 와 dashboard 실행 39 | 2. gradle 의존성 추가 40 | 3. configure property 41 | 4. 걍 쓰세요! 42 | 43 | ## 1. messaging server 와 dashboard 실행 44 | 45 | zsmq 는 두가지 컴포넌트를 제공합니다. 46 | 47 | 아래의 두가지 컴포넌트가 모두 실행되어야 합니다. 48 | 49 | 1. 메시징 서버 50 | 2. 서버 대시보드 51 | 52 | > 자세한 사항은 [wiki 의 zsmq 의 architecture](https://github.com/dhslrl321/zsmq/wiki/ZSMQ's-Architecture) 에서 확인할 수 있습니다. 53 | 54 | 메시지 서버는 message queue 를 관리합니다. message 를 publish 하고 subscribe 하는 대상이 messaging server 입니다. 55 | 56 | dashboard 는 messaging server 에 대한 view 를 제공합니다. 메시지 큐를 확인하고 메시지 큐를 생성/삭제합니다. 57 | 58 | ```shell 59 | docker run --rm -d -p 8290:3000 dhslrl321/zsmq:dashboard.1.0.1 60 | docker run --rm -d -p 8291:8291 dhslrl321/zsmq:server.1.0.0 61 | ``` 62 | 63 | > 만약 포트를 변경해야 하는 일이 생긴다면 issue 에 추가해주세요 64 | 65 | ## 2. gradle 의존성 추가 66 | 67 | zsmq 를 사용하기 위해서는 2가지 의존성이 필요합니다. 68 | 69 | 1. zola-messaging-core 70 | 2. zola-messaging-spring-sdk 71 | 72 | spring 에서 사용할 때는 위의 의존성들을 직접 추가하여 bean 으로 등록할 수 있습니다. 73 | 74 | #### 하지만 ZSMQ 는 위와 같은 복잡한 설정 과정을 생략하고 쉽게 세팅할 수 있도록 spring-boot-starter 를 제공합니다. 75 | 76 | 의존성은 [jitpack](https://jitpack.io/#dhslrl321/zsmq) repository 에서 다운받을 수 있습니다. 77 | 78 | 다음 블록을 `build.gradle` 에 추가하세요. 79 | 80 | ```groovy 81 | repositories { 82 | // ... other maven repository 83 | maven { url 'https://jitpack.io' } 84 | } 85 | 86 | dependencies { 87 | implementation 'com.github.dhslrl321.zsmq:zola-messaging-sprint-boot-starter:1.0.0' 88 | } 89 | ``` 90 | 91 | 수동으로 설정하는 방법을 포함하여 더욱 많은 정보를 얻기 위해서는 [reference guide](https://github.com/dhslrl321/zsmq/wiki/Reference-Guide) 을 확인하세요. 92 | 93 | ## 3. configure property 94 | 95 | 마지막으로 메시징 서버에 대한 정보를 `application.property` 혹은 `application.yml` 에 추가해야 합니다. 96 | 97 | ```yaml 98 | zsmq: 99 | url: http://localhost:8291 100 | listening: false 101 | ``` 102 | 103 | - `zsmq.url` : 메시지 큐를 관리하는 메시징 서버 주소입니다. 104 | - `zsmq.listening` : 리스닝 스레드를 자동으로 등록할지 말지 결정합니다 105 | - 리스너 스레드는 consuming 할 때 사용됩니다. 106 | 107 | ## 4. 걍 쓰세요! 108 | 109 | 이제 모든 설정은 끝났습니다. 110 | 111 | 그냥 사용하세요! 112 | 113 | #### 1. dashboard 에 들어가서 큐를 하나 생성하세요. 설정을 제대로 마쳤다면 `localhost:8290` 로 들어가면 됩니다. 114 | 115 | image 116 | 117 | image 118 | 119 | #### 2. 애플리케이션을 개발하세요 120 | 121 | - 만약 **메시지를 publish** 하고싶다면 `ZolaQueueMessagingTemplate` 를 사용하면 됩니다. 122 | - 만약 **메시지를 consume** 하고싶다면 `@ZolaConsumer` 와 `@ZolaMessageListener` 어노테이션을 사용하면 됩니다. 123 | 124 | ```java 125 | @RequiredArgsConstructor 126 | public class MessageProducer { 127 | 128 | private final ZolaQueueMessageTemplate template; 129 | 130 | public void send() { 131 | template.convertAndSend("MY-QUEUE", "foo"); 132 | } 133 | } 134 | ``` 135 | 136 | ### consume message 137 | 138 | message 를 consume 할 때는 `zsmq.listening` 속성이 true 여야 합니다. 139 | 140 | ```java 141 | @Component 142 | @ZolaConsumer 143 | public class MyConsumer { 144 | 145 | @ZolaMessageListener(queueName = "MY-QUEUE", deletionPolicy = DeletionPolicy.ALWAYS) 146 | public void listen(String message) { 147 | System.out.println("message = " + message); 148 | } 149 | } 150 | ``` 151 | 152 | > 자세한 설정들을 확인하시려면 [example](https://github.com/dhslrl321/zsmq-example) 을 확인하세요. zsmq 를 이용한 간단한 예제가 존재합니다. 153 | 154 | # Motivation 155 | 156 | 어느날, 어떤 학습을 하기 위해서 메시지 큐 하나가 필요했던 적이 있습니다. 157 | 158 | 그 학습에는 메시지 큐에 대한 지식이 깊게 필요하지 않았지만 학습을 이어가기 위해서는 메시지 큐가 꼭 필요했었습니다. 159 | 160 | 그리고 저는 메시지 큐를 설정하고 생성 및 사용하는데에 아주 많은 시간을 사용했습니다. 161 | 162 | 그래서 저는 낮은 성능일지라도 매우 쉽고 빠르게 설정하고 생성할 수 있는 큐를 만들자는 결심을 하였고 이것이 바로 zsmq 의 시작입니다. 163 | 164 | > 'zola' is a korean slang. It means 'utterly', 'extremely', 'super', 'very' 165 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### This is the simplest Message Queue you've ever experienced. 2 | 3 |
4 | 5 | #### [home (engish version)](https://github.com/dhslrl321/zsmq) 6 | 7 | #### [korean version docs](https://github.com/dhslrl321/zsmq/blob/main/README-kor.md) 8 | 9 | 📗Getting Started || Overview || Docs || Change Log 10 | 11 |
12 | 13 | # ZSMQ 14 | 15 | ZSMQ (Zola Simple Message Queue) is a very simple message queue created in java. 16 | 17 | > 'zola' is a korean slang. It means 'utterly', 'extremely', 'super', 'very' 18 | 19 | ZSMQ can be used in a variety of situations (ex, Poc or study), except in the operating environment. 20 | 21 | ZSMQ is the best choice if you don't consider performance. 22 | 23 | ### ZSMQ aims to have 24 | 25 | - **Simplest** 26 | - **Easiest** 27 | - **intuitive configuration** 28 | - **you to focus on other than message queue** 29 | 30 | # Quick Start 31 | 32 | ZSMQ is **fast** and **easy to use**, so we aim to **minimize** the configuration. 33 | 34 | > Check [example](https://github.com/dhslrl321/zsmq-example) to see the detailed configuration; there is a simple example using zsmq. 35 | 36 | You can quickly create a great message queue by following these steps! 37 | 38 | 1. run messaging server and dashboard 39 | 2. gradle dependency 40 | 3. configure property 41 | 4. Just U.S.E it!! 42 | 43 | ## 1. run messaging server and dashboard 44 | 45 | ZSMQ provides two components: 46 | 47 | Both of the following components must be run 48 | 49 | 1. Messaging Server 50 | 2. Message Server Dashboard 51 | 52 | The **Messaging Server** manages the MQ. The destination to publish and subscribe to a message is **Messaging Server**. 53 | 54 | The **dashboard** provides a view of the server. Describe the message queue and crreate/delete the MQ. 55 | 56 | #### You can easily run Zola (ZSMQ) server using docker 57 | 58 | ```shell 59 | docker run --rm -d -p 8290:3000 dhslrl321/zsmq:dashboard.1.0.1 60 | docker run --rm -d -p 8291:8291 dhslrl321/zsmq:server.1.0.0 61 | ``` 62 | 63 | > messaging server and dashboard's port must be 8290, 8291 ! If you need to change the port, please raise it to issue. 64 | 65 | ## 2. gradle dependency 66 | 67 | Two dependencies are required to use zsmq. 68 | 69 | 1. zola-messaging-core 70 | 2. zola-messaging-spring-sdk 71 | 72 | When used with spring framework, You can manually add dependencies one by one. 73 | 74 | #### However, We provide a `spring-boot-starter` so that you can skip the complicated process and set it up easily. 75 | 76 | It can be downloaded from the [jitpack](https://jitpack.io/#dhslrl321/zsmq) repository. 77 | 78 | Add the following blocks to `build.gradle` 79 | 80 | ```groovy 81 | repositories { 82 | // ... other maven repository 83 | maven { url 'https://jitpack.io' } 84 | } 85 | 86 | dependencies { 87 | implementation 'com.github.dhslrl321.zsmq:zola-messaging-spring-boot-starter:${version}' 88 | } 89 | ``` 90 | 91 | You can manually set bin without using the spring boot starter. 92 | 93 | Check the [reference guide](https://github.com/dhslrl321/zsmq/wiki/Reference-Guide) for more information 94 | 95 | ## 3. configure property 96 | 97 | Finally, The url of the Zola Messaging Server must be specified in `application.yml` 98 | 99 | ```yaml 100 | zsmq: 101 | url: http://localhost:8291 102 | listening: false 103 | ``` 104 | 105 | - `zsmq.url` : The **Messaging Server** url that manages the MQ. 106 | - `zsmq.listening` : Decide whether to register the listening thread automatically or not. 107 | - The listener thread is used when consuming. 108 | 109 | ## 4. Just U.S.E it ! 110 | 111 | Now you are done with all settings 112 | 113 | You have to use it as it is. 114 | 115 | #### 1. Open the dashboard, Create a queue. If you are setting properly just go into `localhost:8290` 116 | 117 | image 118 | 119 | image 120 | 121 | #### 2. write the code in your application 122 | 123 | - If you want to **publish a message**, please use `ZolaQueueMessagingTemplate`. 124 | - If you want to **consume a message**, please use '@ZolaConsumer' and `@ZolaMessageListener` 125 | 126 | ### publish message 127 | 128 | ```java 129 | @RequiredArgsConstructor 130 | public class MessageProducer { 131 | 132 | private final ZolaQueueMessageTemplate template; 133 | 134 | public void send() { 135 | template.convertAndSend("MY-QUEUE", "foo"); 136 | } 137 | } 138 | ``` 139 | 140 | ### consume message 141 | 142 | The 'zsmq.listening' attribute must be true when consuming a message. 143 | 144 | ```java 145 | @Component 146 | @ZolaConsumer 147 | public class MyConsumer { 148 | 149 | @ZolaMessageListener(queueName = "MY-QUEUE", deletionPolicy = DeletionPolicy.ALWAYS) 150 | public void listen(String message) { 151 | System.out.println("message = " + message); 152 | } 153 | } 154 | ``` 155 | 156 | > Check [example](https://github.com/dhslrl321/zsmq-example) to see the detailed configuration; there is a simple example using zsmq. 157 | 158 | # Motivation 159 | 160 | One day, I tried to use a message queue simply for studying. 161 | 162 | It was not relevant to the message queue. 163 | 164 | But I spent most of my time setting up message queues(RabbitMQ, SQS) and building infrastructure. 165 | 166 | It was a very tough time just to perform convertAndSend. 167 | 168 | So I decided to **create a server** that has **low performance** but **highly productive** message queue. 169 | 170 | This is the beginning of zsmq. 171 | -------------------------------------------------------------------------------- /dashboard/components/menu/presenter/index.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import AppBar from '@mui/material/AppBar'; 4 | import Box from '@mui/material/Box'; 5 | import CssBaseline from '@mui/material/CssBaseline'; 6 | import Divider from '@mui/material/Divider'; 7 | import Drawer from '@mui/material/Drawer'; 8 | import IconButton from '@mui/material/IconButton'; 9 | import List from '@mui/material/List'; 10 | import ListItem from '@mui/material/ListItem'; 11 | import ListItemButton from '@mui/material/ListItemButton'; 12 | import ListItemIcon from '@mui/material/ListItemIcon'; 13 | import ListItemText from '@mui/material/ListItemText'; 14 | import MenuIcon from '@mui/icons-material/Menu'; 15 | import HomeIcon from '@mui/icons-material/Home'; 16 | import DeleteForeverIcon from '@mui/icons-material/DeleteForever'; 17 | import Toolbar from '@mui/material/Toolbar'; 18 | import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline'; 19 | import Typography from '@mui/material/Typography'; 20 | 21 | import CommandQueueModal from '../../command-dialog/presenter'; 22 | import { useState } from 'react'; 23 | import { createQueueAPI, deleteQueueAPI } from '../../../api-service/command-queue-service'; 24 | import { ERROR_BAD_REQUEST, ERROR_NOT_FOUND } from '../../../commons/constants'; 25 | import { regexNumberAndEnglishAndHyphen } from '../../../commons/validator'; 26 | 27 | const drawerWidth = 240; 28 | 29 | function ResponsiveDrawer({ window, children }) { 30 | const [mobileOpen, setMobileOpen] = useState(false); 31 | 32 | // create queue modal 33 | const [createQModalOpen, setCreateQModalOpen] = useState(false); 34 | const [createQModalInput, setCreateQModalInput] = useState(''); 35 | const handleCreateQModalOpen = () => { 36 | setCreateQModalOpen(true); 37 | }; 38 | const handleCreateQModalClose = () => { 39 | setCreateQModalInput(''); 40 | setCreateQModalOpen(false); 41 | }; 42 | const handleChangeCreateQModalInput = (e) => { 43 | const { value } = e.target; 44 | setCreateQModalInput(value); 45 | }; 46 | const handleOnClickCreate = async () => { 47 | if (!regexNumberAndEnglishAndHyphen(createQModalInput)) { 48 | alert('❌ Error! \nQueue Name must be present and under 40 length, \nTry Again!!'); 49 | return; 50 | } 51 | const res = await createQueueAPI(createQModalInput); 52 | setCreateQModalInput(''); 53 | handleCreateQModalClose(); 54 | }; 55 | 56 | // delete queue modal 57 | const [deleteQueueModalOpen, setDeleteQueueModalOpen] = useState(false); 58 | const [deleteQModalInput, setDeleteQModalInput] = useState(''); 59 | const handleDeleteQModalOpen = () => { 60 | setDeleteQueueModalOpen(true); 61 | }; 62 | const handleDeleteQModalClose = () => { 63 | setDeleteQModalInput(''); 64 | setDeleteQueueModalOpen(false); 65 | }; 66 | const handleChangeDeleteQModalInput = (e) => { 67 | const { value } = e.target; 68 | setDeleteQModalInput(value); 69 | }; 70 | const handleOnClickDelete = async () => { 71 | if (!regexNumberAndEnglishAndHyphen(deleteQModalInput)) { 72 | alert('❌ Error! \nQueue Name must be present and under 40 length, \nTry Again!!'); 73 | return; 74 | } 75 | const res = await deleteQueueAPI(deleteQModalInput); 76 | if (res === ERROR_NOT_FOUND) { 77 | alert(`❌ Error! \n[${deleteQModalInput}] Queue Not Found \nTry Again!!`); 78 | } 79 | setDeleteQModalInput(''); 80 | handleDeleteQModalClose(); 81 | }; 82 | 83 | const handleDrawerToggle = () => { 84 | setMobileOpen(!mobileOpen); 85 | }; 86 | 87 | const drawer = ( 88 |
89 | 99 | 100 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 |
141 | ); 142 | 143 | const container = window !== undefined ? () => window().document.body : undefined; 144 | 145 | return ( 146 | 147 | 148 | 155 | 156 | 163 | 164 | 165 | 166 | Zola Simple Message Queue 167 | 168 | 169 | 170 | 175 | {/* The implementation can be swapped with js to avoid SEO duplication of links. */} 176 | 189 | {drawer} 190 | 191 | 199 | {drawer} 200 | 201 | 202 | 206 | 207 | {children} 208 | 209 | 210 | ); 211 | } 212 | 213 | ResponsiveDrawer.propTypes = { 214 | window: PropTypes.func, 215 | }; 216 | 217 | export default ResponsiveDrawer; 218 | -------------------------------------------------------------------------------- /wiki/Getting Started-kor.md: -------------------------------------------------------------------------------- 1 | #### 해당 내용은 [zsmq-example.git](https://github.com/dhslrl321/zsmq-example) 에서 전체 소스코드를 확인할 수 있습니다. 2 | 3 | # 5분만에 메시징 인프라 구성하기 4 | 5 | ZSMQ 를 이용해서 5분만에 메시징 플랫폼을 구성해봅시다. 6 | 7 | 예제는 다음과 같은 상황을 구현합니다. 8 | 9 | image 10 | 11 | - **order-service** 에서 두가지 이벤트를 발행합니다. 12 | 1. `ORDER-CONFIRMED` 13 | 2. `ORDER-CANCELED` 14 | - **delivery-service** 에서는 두가지 이벤트를 consume 합니다 15 | - `ORDER-CONFIRMED` 의 payload 는 객체 형태입니다. 16 | - `ORDER-CANCELED` 의 payload 는 String 형태입니다. 17 | 18 | # 순서 19 | 20 | 구성 순서는 [README](https://github.com/dhslrl321/zsmq) 에 존재하는 quick start 를 그대로 따릅니다. 21 | 22 | 1. run messaging server and dashboard 23 | 2. gradle dependency 24 | 3. configure property 25 | 4. just U.S.E it 26 | 27 | # 1. run messaging server and dashboard 28 | 29 | 우선 도커를 이용해서 messaging 서버와 dashboard 를 실행시킵니다. 30 | 31 | image 32 | 33 | 잘 실행된 것을 확인합니다. 34 | 35 | # 2. gradle dependency 36 | 37 | gradle 의존성을 추가하기 위해서 두개의 spring application 을 생성해줍니다. 38 | 39 | image 40 | 41 | 그리고 각각의 `build.gradle` 에서 의존성을 추가합니다. 42 | 43 | image 44 | 45 | version 은 [release note](https://github.com/dhslrl321/zsmq/releases) 를 확인해주세요 46 | 47 | # 3. configure property 48 | 49 | 두 애플리케이션의 용도는 서로 다릅니다. 50 | 51 | 1. order-service (produce) 52 | 2. delivery-service (consume) 53 | 54 | ## order-service, produce 55 | 56 | order-service 는 메시지를 발행하는 애플리케이션입니다. 57 | 58 | 다음과 같이 `application.yml` 설정하세요 59 | 60 | image 61 | 62 | `zsmq.listening` 을 false 로 준다는 것은 메시지 리스너 스레드를 실행시키지 않겠다는 뜻입니다. 63 | 64 | #### 이제 애플리케이션을 개발해봅시다! 65 | 66 | ### Order.java 67 | 68 | 간단한 Order 객체를 정의하고 Order 객체를 `delivery-service` 로 전달할 것입니다. 69 | 70 | ```java 71 | @Value(staticConstructor = "of") 72 | public class Order { 73 | String orderId; 74 | String address; 75 | int price; 76 | } 77 | ``` 78 | 79 | ### MessagePublisher.java 80 | 81 | 그리고 간단한 message publisher 를 구현해줍니다. 82 | 83 | ```java 84 | @Component 85 | @RequiredArgsConstructor 86 | public class MessagePublisher { 87 | private final ZolaQueueMessageTemplate template; 88 | 89 | public void sendConfirmedMessage(Order order) { 90 | template.convertAndSend("ORDER-CONFIRMED", order); 91 | } 92 | 93 | public void sendCanceledMessage(String orderId) { 94 | template.convertAndSend("ORDER-CANCELED", orderId); 95 | } 96 | } 97 | ``` 98 | 99 | `ZolaQueueMessageTemplate` 를 이용해서 convert 와 send 를 쉽게 수행할 수 있습니다. 100 | 101 | 객체 형식으로 전달할 수도 있고 단순 문자열 형식으로 전달할 수 있습니다. 102 | 103 | ### OrderController.java 104 | 105 | ```java 106 | @RestController 107 | @RequiredArgsConstructor 108 | public class OrderController { 109 | 110 | private static final Order[] SAMPLE_ORDERS = { 111 | Order.of(UUID.randomUUID().toString(), "Seoul", 125_000_000), 112 | Order.of(UUID.randomUUID().toString(), "New York", 25_602_900), 113 | Order.of(UUID.randomUUID().toString(), "singapore", 5_120_000), 114 | Order.of(UUID.randomUUID().toString(), "tokyo", 9_000_000), 115 | }; 116 | 117 | private final MessagePublisher publisher; 118 | 119 | @GetMapping("/orders/{index}/confirm") 120 | public boolean confirm(@PathVariable int index) { 121 | try { 122 | Order order = SAMPLE_ORDERS[index]; 123 | publisher.sendConfirmedMessage(order); 124 | return true; 125 | } catch (Exception e) { 126 | e.printStackTrace(); 127 | return false; 128 | } 129 | } 130 | 131 | @GetMapping("/orders/{index}/cancel") 132 | public boolean cancel(@PathVariable int index) { 133 | try { 134 | String orderId = SAMPLE_ORDERS[index].getOrderId(); 135 | publisher.sendCanceledMessage(orderId); 136 | return true; 137 | } catch (Exception e) { 138 | e.printStackTrace(); 139 | return false; 140 | } 141 | } 142 | } 143 | ``` 144 | 145 | 간단한 controller 를 구현해줍시다. 146 | 147 | 2개의 api 가 존재합니다. 148 | 149 | 1. `orders/{index}/confirm` : 객체를 publish 150 | 2. `orders/{index}/cancel` : 문자열을 publish 151 | 152 | 이제 message 를 produce 하는 쪽은 모든 준비가 끝났습니다. 153 | 154 | ## delivery-service, consume 155 | 156 | `delivery-service` 는 메시지를 consume 할 것이기 때문에 다음과 같이 yml 에 리스닝 스레드를 활성화시켜줘야 합니다. 157 | 158 | image 159 | 160 | `zsmq.listening` 를 true 로 설정하세요. 161 | 162 | 두개의 큐를 consuming 할 다음 두 processor 를 만들어주세요. 163 | 164 | ### OrderCanceledProcessor.java 165 | 166 | `order-service` 의 `orders/{index}/cancel` 가 호출되면 발행되는 queue 를 consume 합니다. 167 | 168 | ```java 169 | @Component 170 | @ZolaConsumer 171 | @Slf4j 172 | public class OrderCanceledProcessor { 173 | @ZolaMessageListener(queueName="ORDER-CANCELED", deletionPolicy = DeletionPolicy.ALWAYS) 174 | public void listen(String message) { 175 | log.info("order was canceled id: [{}]", message); 176 | } 177 | } 178 | ``` 179 | 180 | `@ZolaMessageListener` 를 사용할 때 메서드의 파라미터는 하나의 String 타입 이어야 합니다. 181 | 182 | 해당 매개변수로 consume 한 데이터를 전달합니다. 183 | 184 | ### OrderConfirmedProcessor.java 185 | 186 | `order-service` 의 `orders/{index}/confirm` 가 호출되면 발행되는 queue 를 consume 합니다. 187 | 188 | ```java 189 | @Component 190 | @ZolaConsumer 191 | @Slf4j 192 | public class OrderConfirmedProcessor { 193 | 194 | @ZolaMessageListener(queueName="ORDER-CONFIRMED", deletionPolicy = DeletionPolicy.ALWAYS) 195 | public void listen(String message) { 196 | log.info("json : {}", message); 197 | Order order = ZolaJsonSerializer.getInstance().deserialize(message, Order.class); 198 | log.info("order was confirmed : id : {}, address: {}, price: {}", order.getOrderId(), order.getAddress(), order.getPrice()); 199 | } 200 | } 201 | ``` 202 | 203 | Object 를 publish 한다면 json 형태로 데이터를 받아옵니다. 204 | 205 | zsmq 에서 제공하는 `ZolaJsonSerializer` 를 사용하면 json 을 쉽게 deserialize 할 수 있습니다. 206 | 207 | # 실행해봅시다! 208 | 209 | 이제 모든 개발이 끝났습니다. 210 | 211 | 실행 결과를 확인해봅시다 212 | 213 | 우선 큐를 만들어야 합니다. 214 | 215 | `localhost:8290` 으로 실행되는 dashboard 에 접속하세요. 216 | 217 | image 218 | 219 | 우리는 2개의 큐를 생성해야 합니다. 오른쪽의 탭에서 QUEUE 생성 메뉴를 클릭하고 큐를 추가하세요 220 | 221 | image 222 | 223 | 두개의 큐를 모두 추가해줍니다. 224 | 225 | image 226 | 227 | 그리고 `order-service` 와 `delivery-service` 를 실행시켜주세요 228 | 229 | 그리고 `order-service` 에 api 를 전송해봅시다. 230 | 231 | image 232 | 233 | 그럼 delivery-service 에는 이전에 로그를 출력하도록 했기 때문에 아래와 같이 로그가 찍히는 것을 확인할 수 있습니다. 234 | 235 | image 236 | 237 | 하나는 json 형태 그대로, 다른 하나는 객체에서 꺼내온대로 잘 출력되었네요. 238 | 239 | 이제 나머지 cancel 도 호출하면 동일하게 결과가 나올 것입니다. 240 | 241 | image 242 | 243 | image 244 | 245 | #### 해당 내용은 [zsmq-example.git](https://github.com/dhslrl321/zsmq-example) 에서 전체 소스코드를 확인할 수 있습니다. 246 | -------------------------------------------------------------------------------- /wiki/Getting Started-eng.md: -------------------------------------------------------------------------------- 1 | #### You can see the full source code at [zsmq-example.git](https://github.com/dhslrl321/zsmq-example). 2 | 3 | # Implements a Application that use Messaging Infrastructure in 5 Minutes 4 | 5 | Let's make a application that use messaging platform in five minutes with ZSMQ. 6 | 7 | The example implements the following situations: 8 | 9 | image 10 | 11 | - **order-service** publish two events. 12 | 1. `ORDER-CONFIRMED` 13 | 2. `ORDER-CANCELED` 14 | - **delivery-service** consume two events. 15 | - `ORDER-CONFIRMED` 's payload is java object 16 | - `ORDER-CANCELED` 's payload is simple text (string) 17 | 18 | # Step 19 | 20 | The configuration order follows the quickstart's step in [README](https://github.com/dhslrl321/zsmq) 21 | 22 | 1. run messaging server and dashboard 23 | 2. gradle dependency 24 | 3. configure property 25 | 4. just U.S.E it 26 | 27 | # 1. run messaging server and dashboard 28 | 29 | First, run the messaging server and the dashboard using the docker. 30 | 31 | image 32 | 33 | Make sure it's done well. 34 | 35 | # 2. gradle dependency 36 | 37 | gradle 의존성을 추가하기 위해서 두개의 spring application 을 생성해줍니다. 38 | 39 | Create two spring applications. 40 | 41 | image 42 | 43 | and add gradle dependencies to `build.gradle` 44 | 45 | image 46 | 47 | check the [release note](https://github.com/dhslrl321/zsmq/releases) for version 48 | 49 | # 3. configure property 50 | 51 | The two applications have different uses. 52 | 53 | 1. order-service (produce) 54 | 2. delivery-service (consume) 55 | 56 | ## order-service, produce 57 | 58 | order-service is an application that publish a message. 59 | 60 | Set 'application.yml' as follows 61 | 62 | image 63 | 64 | Writing 'zsmq.listening' as false means that you will not run the message listener thread. 65 | 66 | #### Now let's implemnts an application! 67 | 68 | ### Order.java 69 | 70 | Implement simple Order objects. We will forward the Order object to 'delivery-service'. 71 | 72 | ```java 73 | @Value(staticConstructor = "of") 74 | public class Order { 75 | String orderId; 76 | String address; 77 | int price; 78 | } 79 | ``` 80 | 81 | ### MessagePublisher.java 82 | 83 | And implements a simple message publisher. 84 | 85 | ```java 86 | @Component 87 | @RequiredArgsConstructor 88 | public class MessagePublisher { 89 | private final ZolaQueueMessageTemplate template; 90 | 91 | public void sendConfirmedMessage(Order order) { 92 | template.convertAndSend("ORDER-CONFIRMED", order); 93 | } 94 | 95 | public void sendCanceledMessage(String orderId) { 96 | template.convertAndSend("ORDER-CANCELED", orderId); 97 | } 98 | } 99 | ``` 100 | 101 | You can easily convert and send using `ZolaQueueMessageTemplate` 102 | 103 | You can forward it in object format or in simple string format. 104 | 105 | ### OrderController.java 106 | 107 | ```java 108 | @RestController 109 | @RequiredArgsConstructor 110 | public class OrderController { 111 | 112 | private static final Order[] SAMPLE_ORDERS = { 113 | Order.of(UUID.randomUUID().toString(), "Seoul", 125_000_000), 114 | Order.of(UUID.randomUUID().toString(), "New York", 25_602_900), 115 | Order.of(UUID.randomUUID().toString(), "singapore", 5_120_000), 116 | Order.of(UUID.randomUUID().toString(), "tokyo", 9_000_000), 117 | }; 118 | 119 | private final MessagePublisher publisher; 120 | 121 | @GetMapping("/orders/{index}/confirm") 122 | public boolean confirm(@PathVariable int index) { 123 | try { 124 | Order order = SAMPLE_ORDERS[index]; 125 | publisher.sendConfirmedMessage(order); 126 | return true; 127 | } catch (Exception e) { 128 | e.printStackTrace(); 129 | return false; 130 | } 131 | } 132 | 133 | @GetMapping("/orders/{index}/cancel") 134 | public boolean cancel(@PathVariable int index) { 135 | try { 136 | String orderId = SAMPLE_ORDERS[index].getOrderId(); 137 | publisher.sendCanceledMessage(orderId); 138 | return true; 139 | } catch (Exception e) { 140 | e.printStackTrace(); 141 | return false; 142 | } 143 | } 144 | } 145 | ``` 146 | 147 | Let's implement a simple controller. 148 | 149 | There are two apis here. 150 | 151 | 1. `orders/{index}/confirm` : publish objects 152 | 2. `orders/{index}/cancel` : publish text, string 153 | 154 | We are now ready to publish the message. 155 | 156 | ## delivery-service, consume 157 | 158 | Because `delivery-service` will consume the message, the yml listening thread must be activated as follows 159 | 160 | image 161 | 162 | set `zsmq.listening` true 163 | 164 | Create the next two processors to consuming 165 | 166 | ### OrderCanceledProcessor.java 167 | 168 | when `order-service` s `orders/{index}/cancel` are called, this will works 169 | 170 | ```java 171 | @Component 172 | @ZolaConsumer 173 | @Slf4j 174 | public class OrderCanceledProcessor { 175 | @ZolaMessageListener(queueName="ORDER-CANCELED", deletionPolicy = DeletionPolicy.ALWAYS) 176 | public void listen(String message) { 177 | log.info("order was canceled id: [{}]", message); 178 | } 179 | } 180 | ``` 181 | 182 | When using '@ZolaMessageListener', the parameters of the method must be String type and single argument. 183 | 184 | Pass consumed data with the corresponding parameters. 185 | 186 | ### OrderConfirmedProcessor.java 187 | 188 | when `order-service` s `orders/{index}/confirm` are called, this will works 189 | 190 | ```java 191 | @Component 192 | @ZolaConsumer 193 | @Slf4j 194 | public class OrderConfirmedProcessor { 195 | 196 | @ZolaMessageListener(queueName="ORDER-CONFIRMED", deletionPolicy = DeletionPolicy.ALWAYS) 197 | public void listen(String message) { 198 | log.info("json : {}", message); 199 | Order order = ZolaJsonSerializer.getInstance().deserialize(message, Order.class); 200 | log.info("order was confirmed : id : {}, address: {}, price: {}", order.getOrderId(), order.getAddress(), order.getPrice()); 201 | } 202 | } 203 | ``` 204 | 205 | If you publish the object, you will receive the data in the form of json. 206 | 207 | 'ZolaJsonSerializer' provided by zsmq makes it easy to deserialize json. 208 | 209 | # Let's run! 210 | 211 | All preparations are now complete. 212 | 213 | Let's check the results 214 | 215 | First, you need to create a queue. 216 | 217 | Access the dashboard running `localhost:8290` 218 | 219 | image 220 | 221 | We need to create two queues. Click on the Create QUEUE menu on the tab on the right and add a queue 222 | 223 | image 224 | 225 | Add both queues. 226 | 227 | image 228 | 229 | Run `order-service` 와 `delivery-service` application 230 | 231 | and `order-service` request api! 232 | 233 | image 234 | 235 | Since the delivery-service was previously required to output the log, you can see that the log is taken as shown below. 236 | 237 | image 238 | 239 | One is printed as it is in json form, and the other is printed as it is taken out of the object. 240 | 241 | Now if you call the rest of the cancel, you will get the same result. 242 | 243 | image 244 | 245 | image 246 | 247 | #### You can see the full source code at [zsmq-example.git](https://github.com/dhslrl321/zsmq-example). 248 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Stop when "xargs" is not available. 209 | if ! command -v xargs >/dev/null 2>&1 210 | then 211 | die "xargs is not available" 212 | fi 213 | 214 | # Use "xargs" to parse quoted args. 215 | # 216 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 217 | # 218 | # In Bash we could simply go: 219 | # 220 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 221 | # set -- "${ARGS[@]}" "$@" 222 | # 223 | # but POSIX shell has neither arrays nor command substitution, so instead we 224 | # post-process each arg (as a line of input to sed) to backslash-escape any 225 | # character that might be a shell metacharacter, then use eval to reverse 226 | # that process (while maintaining the separation between arguments), and wrap 227 | # the whole thing up as a single "set" statement. 228 | # 229 | # This will of course break if any of these variables contains a newline or 230 | # an unmatched quote. 231 | # 232 | 233 | eval "set -- $( 234 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 235 | xargs -n1 | 236 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 237 | tr '\n' ' ' 238 | )" '"$@"' 239 | 240 | exec "$JAVACMD" "$@" 241 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------