├── .idea
├── .gitignore
├── vcs.xml
├── jpa.xml
├── modules.xml
├── notification-engine.iml
├── encodings.xml
├── misc.xml
├── jarRepositories.xml
├── compiler.xml
└── uiDesigner.xml
├── notification-type-1
└── notification-type-1
│ ├── .gitattributes
│ ├── .mvn
│ └── wrapper
│ │ └── maven-wrapper.properties
│ ├── src
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── project
│ │ │ └── notification_type_1
│ │ │ └── NotificationType1ApplicationTests.java
│ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── project
│ │ │ └── notification_type_1
│ │ │ ├── dtos
│ │ │ └── NotificationRequest.java
│ │ │ ├── services
│ │ │ ├── SmsService.java
│ │ │ ├── EmailService.java
│ │ │ ├── WhatsappService.java
│ │ │ └── notifications
│ │ │ │ ├── consumers
│ │ │ │ ├── WhatsappConsumer.java
│ │ │ │ ├── EmailConsumer.java
│ │ │ │ └── SmsConsumer.java
│ │ │ │ └── NotificationService.java
│ │ │ ├── NotificationType1Application.java
│ │ │ └── controllers
│ │ │ └── NotificationController.java
│ │ └── resources
│ │ └── application.yml
│ ├── .gitignore
│ ├── pom.xml
│ ├── mvnw.cmd
│ └── mvnw
├── notification-type-2
└── notification-type-2
│ ├── .gitattributes
│ ├── .mvn
│ └── wrapper
│ │ └── maven-wrapper.properties
│ ├── src
│ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── project
│ │ │ └── notification_type_1
│ │ │ ├── services
│ │ │ └── notifications
│ │ │ │ ├── strategy
│ │ │ │ ├── NotificationHandler.java
│ │ │ │ ├── SmsHandler.java
│ │ │ │ ├── EmailHandler.java
│ │ │ │ ├── WhatsappHandler.java
│ │ │ │ └── NotificationHandlerFactory.java
│ │ │ │ └── consumers
│ │ │ │ └── NotificationConsumers.java
│ │ │ ├── dtos
│ │ │ └── NotificationRequest.java
│ │ │ ├── NotificationType1Application.java
│ │ │ └── controllers
│ │ │ └── NotificationController.java
│ │ └── resources
│ │ └── application.yml
│ ├── .gitignore
│ ├── pom.xml
│ ├── mvnw.cmd
│ └── mvnw
├── notification-type-3
└── notification-type-3
│ ├── .gitattributes
│ ├── .mvn
│ └── wrapper
│ │ └── maven-wrapper.properties
│ ├── src
│ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── project
│ │ │ └── notification_type_3
│ │ │ ├── dtos
│ │ │ └── NotificationRequest.java
│ │ │ ├── NotificationType1Application.java
│ │ │ ├── controllers
│ │ │ └── NotificationController.java
│ │ │ └── services
│ │ │ └── notifications
│ │ │ └── consumers
│ │ │ └── NotificationConsumers.java
│ │ └── resources
│ │ └── application.yml
│ ├── .gitignore
│ ├── pom.xml
│ ├── mvnw.cmd
│ └── mvnw
├── assets
├── type-1.png
├── type2.png
└── type3.png
└── README.md
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/notification-type-1/notification-type-1/.gitattributes:
--------------------------------------------------------------------------------
1 | /mvnw text eol=lf
2 | *.cmd text eol=crlf
3 |
--------------------------------------------------------------------------------
/notification-type-2/notification-type-2/.gitattributes:
--------------------------------------------------------------------------------
1 | /mvnw text eol=lf
2 | *.cmd text eol=crlf
3 |
--------------------------------------------------------------------------------
/notification-type-3/notification-type-3/.gitattributes:
--------------------------------------------------------------------------------
1 | /mvnw text eol=lf
2 | *.cmd text eol=crlf
3 |
--------------------------------------------------------------------------------
/assets/type-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mina-Girgis/Notification-Service-Architecture-Design-With-Spring-Boot-RabbitMQ-And-Spring-Cloud-Stream/HEAD/assets/type-1.png
--------------------------------------------------------------------------------
/assets/type2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mina-Girgis/Notification-Service-Architecture-Design-With-Spring-Boot-RabbitMQ-And-Spring-Cloud-Stream/HEAD/assets/type2.png
--------------------------------------------------------------------------------
/assets/type3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mina-Girgis/Notification-Service-Architecture-Design-With-Spring-Boot-RabbitMQ-And-Spring-Cloud-Stream/HEAD/assets/type3.png
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/notification-type-1/notification-type-1/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionType=only-script
2 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip
3 |
--------------------------------------------------------------------------------
/notification-type-2/notification-type-2/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionType=only-script
2 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip
3 |
--------------------------------------------------------------------------------
/notification-type-3/notification-type-3/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionType=only-script
2 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip
3 |
--------------------------------------------------------------------------------
/.idea/jpa.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/notification-type-2/notification-type-2/src/main/java/com/project/notification_type_1/services/notifications/strategy/NotificationHandler.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_1.services.notifications.strategy;
2 |
3 | import com.project.notification_type_1.dtos.NotificationRequest;
4 |
5 | public interface NotificationHandler {
6 | void handle(NotificationRequest request);
7 | }
--------------------------------------------------------------------------------
/.idea/notification-engine.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/notification-type-1/notification-type-1/src/test/java/com/project/notification_type_1/NotificationType1ApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_1;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class NotificationType1ApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/notification-type-1/notification-type-1/src/main/java/com/project/notification_type_1/dtos/NotificationRequest.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_1.dtos;
2 |
3 | import lombok.Data;
4 |
5 | @Data
6 | public class NotificationRequest {
7 | private String recipient;
8 | private String subject;
9 | private String message;
10 | private String channel; // email | sms | whatsapp
11 | private Integer priority;
12 | }
13 |
--------------------------------------------------------------------------------
/notification-type-2/notification-type-2/src/main/java/com/project/notification_type_1/dtos/NotificationRequest.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_1.dtos;
2 | import lombok.Data;
3 |
4 | @Data
5 | public class NotificationRequest {
6 | private String recipient;
7 | private String subject;
8 | private String message;
9 | private String channel; // email | sms | whatsapp
10 | private String priority; // high | normal | low
11 | }
12 |
--------------------------------------------------------------------------------
/notification-type-3/notification-type-3/src/main/java/com/project/notification_type_3/dtos/NotificationRequest.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_3.dtos;
2 | import lombok.Data;
3 |
4 | @Data
5 | public class NotificationRequest {
6 | private String recipient;
7 | private String subject;
8 | private String message;
9 | private String channel; // email | sms | whatsapp
10 | private String priority; // high | normal | low
11 | }
12 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/notification-type-1/notification-type-1/src/main/java/com/project/notification_type_1/services/SmsService.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_1.services;
2 |
3 | import com.project.notification_type_1.dtos.NotificationRequest;
4 | import org.springframework.stereotype.Service;
5 |
6 | @Service
7 | public class SmsService {
8 |
9 | public void sendSms(NotificationRequest request) {
10 | System.out.println("📧 Processing SMS: " + request);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/notification-type-1/notification-type-1/src/main/java/com/project/notification_type_1/services/EmailService.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_1.services;
2 |
3 | import com.project.notification_type_1.dtos.NotificationRequest;
4 | import org.springframework.stereotype.Service;
5 |
6 | @Service
7 | public class EmailService {
8 |
9 | public void sendEmail(NotificationRequest request) {
10 | System.out.println("📧 Processing Email: " + request);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/notification-type-1/notification-type-1/src/main/java/com/project/notification_type_1/services/WhatsappService.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_1.services;
2 |
3 | import com.project.notification_type_1.dtos.NotificationRequest;
4 | import org.springframework.stereotype.Service;
5 |
6 | @Service
7 | public class WhatsappService {
8 |
9 | public void sendWhatsapp(NotificationRequest request) {
10 | System.out.println("📧 Processing Whatsapp: " + request);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/notification-type-1/notification-type-1/src/main/java/com/project/notification_type_1/NotificationType1Application.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_1;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class NotificationType1Application {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(NotificationType1Application.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/notification-type-2/notification-type-2/src/main/java/com/project/notification_type_1/NotificationType1Application.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_1;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class NotificationType1Application {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(NotificationType1Application.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/notification-type-3/notification-type-3/src/main/java/com/project/notification_type_3/NotificationType1Application.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_3;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class NotificationType1Application {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(NotificationType1Application.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/notification-type-2/notification-type-2/src/main/java/com/project/notification_type_1/services/notifications/strategy/SmsHandler.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_1.services.notifications.strategy;
2 |
3 | import com.project.notification_type_1.dtos.NotificationRequest;
4 | import org.springframework.stereotype.Service;
5 |
6 | @Service("sms")
7 | class SmsHandler implements NotificationHandler {
8 | @Override
9 | public void handle(NotificationRequest request) {
10 | System.out.println("📱 Sending SMS to " + request.getRecipient());
11 | }
12 | }
--------------------------------------------------------------------------------
/notification-type-2/notification-type-2/src/main/java/com/project/notification_type_1/services/notifications/strategy/EmailHandler.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_1.services.notifications.strategy;
2 |
3 | import com.project.notification_type_1.dtos.NotificationRequest;
4 | import org.springframework.stereotype.Service;
5 |
6 | @Service("email")
7 | class EmailHandler implements NotificationHandler {
8 | @Override
9 | public void handle(NotificationRequest request) {
10 | System.out.println("📧 Sending EMAIL to " + request.getRecipient());
11 | }
12 | }
--------------------------------------------------------------------------------
/notification-type-1/notification-type-1/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | .mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**/target/
5 | !**/src/test/**/target/
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 | .sts4-cache
15 |
16 | ### IntelliJ IDEA ###
17 | .idea
18 | *.iws
19 | *.iml
20 | *.ipr
21 |
22 | ### NetBeans ###
23 | /nbproject/private/
24 | /nbbuild/
25 | /dist/
26 | /nbdist/
27 | /.nb-gradle/
28 | build/
29 | !**/src/main/**/build/
30 | !**/src/test/**/build/
31 |
32 | ### VS Code ###
33 | .vscode/
34 |
--------------------------------------------------------------------------------
/notification-type-2/notification-type-2/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | .mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**/target/
5 | !**/src/test/**/target/
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 | .sts4-cache
15 |
16 | ### IntelliJ IDEA ###
17 | .idea
18 | *.iws
19 | *.iml
20 | *.ipr
21 |
22 | ### NetBeans ###
23 | /nbproject/private/
24 | /nbbuild/
25 | /dist/
26 | /nbdist/
27 | /.nb-gradle/
28 | build/
29 | !**/src/main/**/build/
30 | !**/src/test/**/build/
31 |
32 | ### VS Code ###
33 | .vscode/
34 |
--------------------------------------------------------------------------------
/notification-type-2/notification-type-2/src/main/java/com/project/notification_type_1/services/notifications/strategy/WhatsappHandler.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_1.services.notifications.strategy;
2 |
3 | import com.project.notification_type_1.dtos.NotificationRequest;
4 | import org.springframework.stereotype.Service;
5 |
6 | @Service("whatsapp")
7 | class WhatsappHandler implements NotificationHandler {
8 | @Override
9 | public void handle(NotificationRequest request) {
10 | System.out.println("💬 Sending WhatsApp to " + request.getRecipient());
11 | }
12 | }
--------------------------------------------------------------------------------
/notification-type-3/notification-type-3/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | .mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**/target/
5 | !**/src/test/**/target/
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 | .sts4-cache
15 |
16 | ### IntelliJ IDEA ###
17 | .idea
18 | *.iws
19 | *.iml
20 | *.ipr
21 |
22 | ### NetBeans ###
23 | /nbproject/private/
24 | /nbbuild/
25 | /dist/
26 | /nbdist/
27 | /.nb-gradle/
28 | build/
29 | !**/src/main/**/build/
30 | !**/src/test/**/build/
31 |
32 | ### VS Code ###
33 | .vscode/
34 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/notification-type-2/notification-type-2/src/main/java/com/project/notification_type_1/services/notifications/strategy/NotificationHandlerFactory.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_1.services.notifications.strategy;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import org.apache.catalina.core.ApplicationContext;
5 | import org.springframework.stereotype.Component;
6 |
7 | import java.util.Map;
8 |
9 | @Component
10 | @RequiredArgsConstructor
11 | public class NotificationHandlerFactory {
12 | private final Map handlers;
13 |
14 | public NotificationHandler getHandler(String type) {
15 | NotificationHandler handler = handlers.get(type);
16 | if (handler == null) {
17 | throw new IllegalArgumentException("No handler found for type: " + type);
18 | }
19 | return handler;
20 | }
21 | }
--------------------------------------------------------------------------------
/notification-type-1/notification-type-1/src/main/java/com/project/notification_type_1/services/notifications/consumers/WhatsappConsumer.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_1.services.notifications.consumers;
2 |
3 | import com.project.notification_type_1.dtos.NotificationRequest;
4 | import com.project.notification_type_1.services.WhatsappService;
5 | import lombok.RequiredArgsConstructor;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 |
9 | import java.util.function.Consumer;
10 | @Configuration
11 | @RequiredArgsConstructor
12 | public class WhatsappConsumer {
13 | private final WhatsappService whatsappService;
14 |
15 | // WhatsApp consumer
16 | @Bean
17 | public Consumer whatsappConsumer() {
18 | return whatsappService::sendWhatsapp;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/notification-type-1/notification-type-1/src/main/java/com/project/notification_type_1/services/notifications/consumers/EmailConsumer.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_1.services.notifications.consumers;
2 |
3 | import com.project.notification_type_1.dtos.NotificationRequest;
4 | import com.project.notification_type_1.services.EmailService;
5 | import lombok.RequiredArgsConstructor;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 |
9 | import java.util.function.Consumer;
10 |
11 | @Configuration
12 | @RequiredArgsConstructor
13 | public class EmailConsumer {
14 | private final EmailService emailService;
15 |
16 | // SMS consumer
17 | @Bean
18 | public Consumer smsConsumer() {
19 | // call sms service here
20 | return this.emailService::sendEmail;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/notification-type-1/notification-type-1/src/main/java/com/project/notification_type_1/services/notifications/consumers/SmsConsumer.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_1.services.notifications.consumers;
2 |
3 | import com.project.notification_type_1.dtos.NotificationRequest;
4 | import com.project.notification_type_1.services.SmsService;
5 | import lombok.RequiredArgsConstructor;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 |
9 | import java.util.function.Consumer;
10 |
11 | @Configuration
12 | @RequiredArgsConstructor
13 | public class SmsConsumer {
14 | private final SmsService smsService; // it can be multiple email providers
15 | // SMS consumer
16 | @Bean
17 | public Consumer smsConsumer() {
18 | // call sms service here
19 | return this.smsService::sendSms;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/notification-type-1/notification-type-1/src/main/java/com/project/notification_type_1/controllers/NotificationController.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_1.controllers;
2 |
3 | import com.project.notification_type_1.dtos.NotificationRequest;
4 | import com.project.notification_type_1.services.notifications.NotificationService;
5 | import lombok.RequiredArgsConstructor;
6 | import org.springframework.http.ResponseEntity;
7 | import org.springframework.web.bind.annotation.PostMapping;
8 | import org.springframework.web.bind.annotation.RequestBody;
9 | import org.springframework.web.bind.annotation.RequestMapping;
10 | import org.springframework.web.bind.annotation.RestController;
11 |
12 | @RestController
13 | @RequestMapping("/notifications")
14 | @RequiredArgsConstructor
15 | public class NotificationController {
16 | private final NotificationService service;
17 |
18 | @PostMapping("/notify")
19 | public ResponseEntity sendNotification(@RequestBody NotificationRequest request) {
20 | service.send(request);
21 | return ResponseEntity.ok("Notification sent to channel: " + request.getChannel());
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/notification-type-1/notification-type-1/src/main/java/com/project/notification_type_1/services/notifications/NotificationService.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_1.services.notifications;
2 |
3 | import com.project.notification_type_1.dtos.NotificationRequest;
4 | import lombok.RequiredArgsConstructor;
5 | import org.springframework.cloud.stream.function.StreamBridge;
6 | import org.springframework.messaging.Message;
7 | import org.springframework.messaging.support.MessageBuilder;
8 | import org.springframework.stereotype.Service;
9 |
10 | @Service
11 | @RequiredArgsConstructor
12 | public class NotificationService {
13 |
14 | private final StreamBridge streamBridge;
15 |
16 | public void send(NotificationRequest request) {
17 | // push to notification.exchange queue
18 | Message message = MessageBuilder
19 | .withPayload(request)
20 | .setHeader("routingKey", request.getChannel()) // routing key
21 | .setHeader("priority", request.getPriority() != null ? request.getPriority() : 2)
22 | .build();
23 | streamBridge.send("notificationProducer-out-0", message);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/notification-type-2/notification-type-2/src/main/java/com/project/notification_type_1/services/notifications/consumers/NotificationConsumers.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_1.services.notifications.consumers;
2 |
3 | import com.project.notification_type_1.dtos.NotificationRequest;
4 | import com.project.notification_type_1.services.notifications.strategy.NotificationHandlerFactory;
5 | import lombok.RequiredArgsConstructor;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.stereotype.Service;
8 |
9 | import java.util.function.Consumer;
10 |
11 | @Service
12 | @RequiredArgsConstructor
13 | public class NotificationConsumers {
14 |
15 | private final NotificationHandlerFactory notificationHandlerFactory;
16 |
17 | @Bean
18 | public Consumer highConsumer() {
19 | return request -> notificationHandlerFactory.getHandler(request.getChannel()).handle(request);
20 | }
21 |
22 | @Bean
23 | public Consumer normalConsumer() {
24 | return request -> notificationHandlerFactory.getHandler(request.getChannel()).handle(request);
25 | }
26 |
27 | @Bean
28 | public Consumer lowConsumer() {
29 | return request -> notificationHandlerFactory.getHandler(request.getChannel()).handle(request);
30 | }
31 | }
--------------------------------------------------------------------------------
/notification-type-2/notification-type-2/src/main/java/com/project/notification_type_1/controllers/NotificationController.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_1.controllers;
2 |
3 | import com.project.notification_type_1.dtos.NotificationRequest;
4 | import lombok.RequiredArgsConstructor;
5 | import org.springframework.cloud.stream.function.StreamBridge;
6 | import org.springframework.http.ResponseEntity;
7 | import org.springframework.integration.support.MessageBuilder;
8 | import org.springframework.web.bind.annotation.PostMapping;
9 | import org.springframework.web.bind.annotation.RequestBody;
10 | import org.springframework.web.bind.annotation.RequestMapping;
11 | import org.springframework.web.bind.annotation.RestController;
12 |
13 | @RestController
14 | @RequestMapping("/notifications")
15 | @RequiredArgsConstructor
16 | public class NotificationController {
17 |
18 | private StreamBridge streamBridge;
19 |
20 | @PostMapping("/notify")
21 | public ResponseEntity sendNotification(@RequestBody NotificationRequest request) {
22 | String binding = "notification." + request.getPriority();
23 | streamBridge.send(binding, MessageBuilder.withPayload(request).setHeader("type", request.getChannel()).build());
24 | return ResponseEntity.ok("Message sent to " + request.getPriority() + " queue");
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/notification-type-3/notification-type-3/src/main/java/com/project/notification_type_3/controllers/NotificationController.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_3.controllers;
2 |
3 | import com.project.notification_type_3.dtos.NotificationRequest;
4 | import lombok.RequiredArgsConstructor;
5 | import org.springframework.cloud.stream.function.StreamBridge;
6 | import org.springframework.http.ResponseEntity;
7 | import org.springframework.integration.support.MessageBuilder;
8 | import org.springframework.web.bind.annotation.PostMapping;
9 | import org.springframework.web.bind.annotation.RequestBody;
10 | import org.springframework.web.bind.annotation.RequestMapping;
11 | import org.springframework.web.bind.annotation.RestController;
12 |
13 | @RestController
14 | @RequestMapping("/notifications")
15 | @RequiredArgsConstructor
16 | public class NotificationController {
17 |
18 | private StreamBridge streamBridge;
19 |
20 | @PostMapping("/notify")
21 | public ResponseEntity sendNotification(@RequestBody NotificationRequest request) {
22 | String binding = request.getChannel().toLowerCase() + capitalize(request.getPriority()) + "Consumer-in-0";
23 | streamBridge.send(binding, MessageBuilder.withPayload(request).build());
24 | return ResponseEntity.ok("Notification sent to " + binding);
25 | }
26 |
27 | private String capitalize(String str) {
28 | if (str == null || str.isEmpty()) return str;
29 | return str.substring(0, 1).toUpperCase() + str.substring(1).toLowerCase();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/notification-type-2/notification-type-2/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | rabbitmq:
3 | host: localhost
4 | port: 5672
5 | username: guest
6 | password: guest
7 |
8 | cloud:
9 | stream:
10 | defaultBinder: rabbit
11 | function:
12 | definition: highConsumer;normalConsumer;lowConsumer
13 |
14 | bindings:
15 | # HIGH PRIORITY channel
16 | highConsumer-in-0:
17 | destination: notification.high
18 | group: high-group
19 | binder: rabbit
20 | # NORMAL PRIORITY channel
21 | normalConsumer-in-0:
22 | destination: notification.normal
23 | group: normal-group
24 | binder: rabbit
25 | # LOW PRIORITY channel
26 | lowConsumer-in-0:
27 | destination: notification.low
28 | group: low-group
29 | binder: rabbit
30 |
31 | rabbit:
32 | bindings:
33 | highConsumer-in-0:
34 | consumer:
35 | concurrency: 5 # more workers for high-priority
36 | prefetch: 10
37 | autoBindDlq: true
38 | republishToDlq: true
39 |
40 | normalConsumer-in-0:
41 | consumer:
42 | concurrency: 3
43 | prefetch: 5
44 | autoBindDlq: true
45 | republishToDlq: true
46 |
47 | lowConsumer-in-0:
48 | consumer:
49 | concurrency: 1 # fewer workers for low-priority
50 | prefetch: 2
51 | autoBindDlq: true
52 | republishToDlq: true
53 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/notification-type-3/notification-type-3/src/main/java/com/project/notification_type_3/services/notifications/consumers/NotificationConsumers.java:
--------------------------------------------------------------------------------
1 | package com.project.notification_type_3.services.notifications.consumers;
2 |
3 | import com.project.notification_type_3.dtos.NotificationRequest;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.stereotype.Component;
6 |
7 | import java.util.function.Consumer;
8 |
9 | @Component
10 | public class NotificationConsumers {
11 |
12 | // --- Email Consumers ---
13 | @Bean
14 | public Consumer emailHighConsumer() {
15 | return request -> handleEmail(request, "HIGH");
16 | }
17 |
18 | @Bean
19 | public Consumer emailNormalConsumer() {
20 | return request -> handleEmail(request, "NORMAL");
21 | }
22 |
23 | @Bean
24 | public Consumer emailLowConsumer() {
25 | return request -> handleEmail(request, "LOW");
26 | }
27 |
28 | // --- SMS Consumers ---
29 | @Bean
30 | public Consumer smsHighConsumer() {
31 | return request -> handleSms(request, "HIGH");
32 | }
33 |
34 | @Bean
35 | public Consumer smsNormalConsumer() {
36 | return request -> handleSms(request, "NORMAL");
37 | }
38 |
39 | @Bean
40 | public Consumer smsLowConsumer() {
41 | return request -> handleSms(request, "LOW");
42 | }
43 |
44 | // --- WhatsApp Consumers ---
45 | @Bean
46 | public Consumer whatsappHighConsumer() {
47 | return request -> handleWhatsapp(request, "HIGH");
48 | }
49 |
50 | @Bean
51 | public Consumer whatsappNormalConsumer() {
52 | return request -> handleWhatsapp(request, "NORMAL");
53 | }
54 |
55 | @Bean
56 | public Consumer whatsappLowConsumer() {
57 | return request -> handleWhatsapp(request, "LOW");
58 | }
59 |
60 | // --- Handlers ---
61 | private void handleEmail(NotificationRequest request, String priority) {
62 | System.out.println("Sending EMAIL [" + priority + "] to " + request.getRecipient() + ": " + request.getMessage());
63 | // Add actual email sending logic here
64 | }
65 |
66 | private void handleSms(NotificationRequest request, String priority) {
67 | System.out.println("Sending SMS [" + priority + "] to " + request.getRecipient() + ": " + request.getMessage());
68 | // Add actual SMS sending logic here
69 | }
70 |
71 | private void handleWhatsapp(NotificationRequest request, String priority) {
72 | System.out.println("Sending WHATSAPP [" + priority + "] to " + request.getRecipient() + ": " + request.getMessage());
73 | // Add actual WhatsApp sending logic here
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/notification-type-1/notification-type-1/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 |
3 | rabbitmq:
4 | host: localhost
5 | port: 5672
6 | username: guest
7 | password: guest
8 |
9 | cloud:
10 | stream:
11 | defaultBinder: rabbit
12 | function:
13 | definition: notificationProducer;emailConsumer;smsConsumer;whatsappConsumer
14 |
15 | bindings:
16 | # Producer output → goes to central exchange
17 | notificationProducer-out-0:
18 | destination: notification.exchange
19 | binder: rabbit
20 |
21 | # Email consumer binding
22 | emailConsumer-in-0:
23 | destination: notification.exchange # bind to exchange, not queue directly
24 | group: email-group
25 | binder: rabbit
26 | consumer:
27 | maxAttempts: 5 # retry 5 times
28 | backOffInitialInterval: 1000 # 1s before first retry
29 | backOffMaxInterval: 10000 # max 10s
30 | backOffMultiplier: 2.0 # exponential backoff
31 |
32 | # SMS consumer binding
33 | smsConsumer-in-0:
34 | destination: notification.exchange
35 | group: sms-group
36 | binder: rabbit
37 | consumer:
38 | maxAttempts: 5
39 | backOffInitialInterval: 2000
40 | backOffMaxInterval: 15000
41 | backOffMultiplier: 2.0
42 |
43 | # WhatsApp consumer binding
44 | whatsappConsumer-in-0:
45 | destination: notification.exchange
46 | group: whatsapp-group
47 | binder: rabbit
48 | consumer:
49 | maxAttempts: 2
50 | backOffInitialInterval: 500
51 | backOffMaxInterval: 5000
52 | backOffMultiplier: 1.5
53 |
54 | rabbit:
55 | bindings:
56 | notificationProducer-out-0:
57 | producer:
58 | declareExchange: true
59 | exchangeType: direct
60 | routingKeyExpression: headers['routingKey']
61 |
62 | # Email queue
63 | emailConsumer-in-0:
64 | consumer:
65 | queueNameGroupOnly: true
66 | bindingRoutingKey: email # bind queue to exchange with routing key "email"
67 | autoBindDlq: true # enable DLQ
68 | republishToDlq: true # failed messages go to .dlq
69 | args:
70 | x-max-priority: 9
71 | x-message-ttl: 60000
72 |
73 | # SMS queue
74 | smsConsumer-in-0:
75 | consumer:
76 | queueNameGroupOnly: true
77 | bindingRoutingKey: sms
78 | autoBindDlq: true
79 | republishToDlq: true
80 | args:
81 | x-max-priority: 9
82 | x-message-ttl: 60000
83 |
84 | # WhatsApp queue
85 | whatsappConsumer-in-0:
86 | consumer:
87 | queueNameGroupOnly: true
88 | bindingRoutingKey: whatsapp
89 | autoBindDlq: true
90 | republishToDlq: true
91 | args:
92 | x-max-priority: 9
93 | x-message-ttl: 60000
94 |
--------------------------------------------------------------------------------
/notification-type-1/notification-type-1/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 3.5.5
9 |
10 |
11 | com.project
12 | notification-type-1
13 | 0.0.1-SNAPSHOT
14 | notification-type-1
15 | Demo project for Spring Boot
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | 21
31 |
32 |
33 |
34 | org.springframework.boot
35 | spring-boot-starter-amqp
36 |
37 |
38 | org.springframework.boot
39 | spring-boot-starter-data-jpa
40 |
41 |
42 | org.springframework.boot
43 | spring-boot-starter-web
44 |
45 |
46 |
47 | com.h2database
48 | h2
49 | runtime
50 |
51 |
52 | org.projectlombok
53 | lombok
54 | true
55 |
56 |
57 | org.springframework.boot
58 | spring-boot-starter-test
59 | test
60 |
61 |
62 | org.springframework.amqp
63 | spring-rabbit-test
64 | test
65 |
66 |
67 | org.springframework.cloud
68 | spring-cloud-stream
69 |
70 |
71 | org.springframework.cloud
72 | spring-cloud-stream-binder-rabbit
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | org.springframework.cloud
81 | spring-cloud-dependencies
82 | 2023.0.3
83 | pom
84 | import
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | org.apache.maven.plugins
93 | maven-compiler-plugin
94 |
95 |
96 |
97 | org.projectlombok
98 | lombok
99 |
100 |
101 |
102 |
103 |
104 | org.springframework.boot
105 | spring-boot-maven-plugin
106 |
107 |
108 |
109 | org.projectlombok
110 | lombok
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/notification-type-2/notification-type-2/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 3.5.5
9 |
10 |
11 | com.project
12 | notification-type-1
13 | 0.0.1-SNAPSHOT
14 | notification-type-2
15 | Demo project for Spring Boot
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | 21
31 |
32 |
33 |
34 | org.springframework.boot
35 | spring-boot-starter-amqp
36 |
37 |
38 | org.springframework.boot
39 | spring-boot-starter-data-jpa
40 |
41 |
42 | org.springframework.boot
43 | spring-boot-starter-web
44 |
45 |
46 |
47 | com.h2database
48 | h2
49 | runtime
50 |
51 |
52 | org.projectlombok
53 | lombok
54 | true
55 |
56 |
57 | org.springframework.boot
58 | spring-boot-starter-test
59 | test
60 |
61 |
62 | org.springframework.amqp
63 | spring-rabbit-test
64 | test
65 |
66 |
67 | org.springframework.cloud
68 | spring-cloud-stream
69 |
70 |
71 | org.springframework.cloud
72 | spring-cloud-stream-binder-rabbit
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | org.springframework.cloud
81 | spring-cloud-dependencies
82 | 2023.0.3
83 | pom
84 | import
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | org.apache.maven.plugins
93 | maven-compiler-plugin
94 |
95 |
96 |
97 | org.projectlombok
98 | lombok
99 |
100 |
101 |
102 |
103 |
104 | org.springframework.boot
105 | spring-boot-maven-plugin
106 |
107 |
108 |
109 | org.projectlombok
110 | lombok
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/notification-type-3/notification-type-3/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 3.5.5
9 |
10 |
11 | com.project
12 | notification-type-1
13 | 0.0.1-SNAPSHOT
14 | notification-type-3
15 | Demo project for Spring Boot
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | 21
31 |
32 |
33 |
34 | org.springframework.boot
35 | spring-boot-starter-amqp
36 |
37 |
38 | org.springframework.boot
39 | spring-boot-starter-data-jpa
40 |
41 |
42 | org.springframework.boot
43 | spring-boot-starter-web
44 |
45 |
46 |
47 | com.h2database
48 | h2
49 | runtime
50 |
51 |
52 | org.projectlombok
53 | lombok
54 | true
55 |
56 |
57 | org.springframework.boot
58 | spring-boot-starter-test
59 | test
60 |
61 |
62 | org.springframework.amqp
63 | spring-rabbit-test
64 | test
65 |
66 |
67 | org.springframework.cloud
68 | spring-cloud-stream
69 |
70 |
71 | org.springframework.cloud
72 | spring-cloud-stream-binder-rabbit
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | org.springframework.cloud
81 | spring-cloud-dependencies
82 | 2023.0.3
83 | pom
84 | import
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | org.apache.maven.plugins
93 | maven-compiler-plugin
94 |
95 |
96 |
97 | org.projectlombok
98 | lombok
99 |
100 |
101 |
102 |
103 |
104 | org.springframework.boot
105 | spring-boot-maven-plugin
106 |
107 |
108 |
109 | org.projectlombok
110 | lombok
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/notification-type-3/notification-type-3/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | rabbitmq:
3 | host: localhost
4 | port: 5672
5 | username: guest
6 | password: guest
7 |
8 | cloud:
9 | stream:
10 | defaultBinder: rabbit
11 | function:
12 | definition: emailHighConsumer;emailNormalConsumer;emailLowConsumer;
13 | smsHighConsumer;smsNormalConsumer;smsLowConsumer;
14 | whatsappHighConsumer;whatsappNormalConsumer;whatsappLowConsumer
15 |
16 | bindings:
17 | # Email queues
18 | emailHighConsumer-in-0:
19 | destination: email.high
20 | group: email-high-group
21 | binder: rabbit
22 | emailNormalConsumer-in-0:
23 | destination: email.normal
24 | group: email-normal-group
25 | binder: rabbit
26 | emailLowConsumer-in-0:
27 | destination: email.low
28 | group: email-low-group
29 | binder: rabbit
30 |
31 | # SMS queues
32 | smsHighConsumer-in-0:
33 | destination: sms.high
34 | group: sms-high-group
35 | binder: rabbit
36 | smsNormalConsumer-in-0:
37 | destination: sms.normal
38 | group: sms-normal-group
39 | binder: rabbit
40 | smsLowConsumer-in-0:
41 | destination: sms.low
42 | group: sms-low-group
43 | binder: rabbit
44 |
45 | # WhatsApp queues
46 | whatsappHighConsumer-in-0:
47 | destination: whatsapp.high
48 | group: whatsapp-high-group
49 | binder: rabbit
50 | whatsappNormalConsumer-in-0:
51 | destination: whatsapp.normal
52 | group: whatsapp-normal-group
53 | binder: rabbit
54 | whatsappLowConsumer-in-0:
55 | destination: whatsapp.low
56 | group: whatsapp-low-group
57 | binder: rabbit
58 |
59 | rabbit:
60 | bindings:
61 | # Example: configure concurrency & DLQ per queue
62 | emailHighConsumer-in-0:
63 | consumer:
64 | concurrency: 5
65 | prefetch: 10
66 | autoBindDlq: true
67 | republishToDlq: true
68 | emailNormalConsumer-in-0:
69 | consumer:
70 | concurrency: 3
71 | prefetch: 5
72 | autoBindDlq: true
73 | republishToDlq: true
74 | emailLowConsumer-in-0:
75 | consumer:
76 | concurrency: 1
77 | prefetch: 2
78 | autoBindDlq: true
79 | republishToDlq: true
80 |
81 | smsHighConsumer-in-0:
82 | consumer:
83 | concurrency: 5
84 | prefetch: 10
85 | autoBindDlq: true
86 | republishToDlq: true
87 | smsNormalConsumer-in-0:
88 | consumer:
89 | concurrency: 3
90 | prefetch: 5
91 | autoBindDlq: true
92 | republishToDlq: true
93 | smsLowConsumer-in-0:
94 | consumer:
95 | concurrency: 1
96 | prefetch: 2
97 | autoBindDlq: true
98 | republishToDlq: true
99 |
100 | whatsappHighConsumer-in-0:
101 | consumer:
102 | concurrency: 5
103 | prefetch: 10
104 | autoBindDlq: true
105 | republishToDlq: true
106 | whatsappNormalConsumer-in-0:
107 | consumer:
108 | concurrency: 3
109 | prefetch: 5
110 | autoBindDlq: true
111 | republishToDlq: true
112 | whatsappLowConsumer-in-0:
113 | consumer:
114 | concurrency: 1
115 | prefetch: 2
116 | autoBindDlq: true
117 | republishToDlq: true
118 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Notification Service
2 |
3 | ## 📌 Project Description
4 | This project demonstrates how to build a **scalable notification service** using **Spring Boot**, **Spring Cloud Stream**, and **RabbitMQ**.
5 |
6 | The service is designed to handle different workloads and scenarios by providing **three architecture patterns**. Each type is tailored for specific use cases such as **low volume**, **high throughput**, or **priority-based** messaging.
7 |
8 | ---
9 |
10 | ## 🗂️ Index of Architectures
11 | 1. [Type-1: Channel per type with priority](#type-1-channel-per-type-with-priority)
12 | 2. [Type-2: Three Priority Channels (High, Normal, Low)](#type-2-three-priority-channels-high-normal-low)
13 | 3. [Type-3: Separate queues per type + per priority](#type-3-separate-queues-per-type--per-priority)
14 |
15 | ---
16 |
17 | ## 🔹 Type-1: Channel per type with priority
18 | 
19 |
20 | In this setup:
21 | - All messages are published to a **single central exchange** (`notification.exchange`).
22 | - Each message includes a **routingKey** header (`email`, `sms`, `whatsapp`).
23 | - RabbitMQ uses these routing keys to deliver messages into the correct queue.
24 | - Consumers are bound to specific queues and process messages independently.
25 |
26 | ## ✨ Features
27 | - Supports different types of notifications (**Email, SMS, WhatsApp**)
28 | - Configurable **retry policies** for each consumer
29 | - **Dead Letter Queues (DLQ)** for failed messages
30 | - **Priority queues** to process urgent messages first
31 |
32 |
33 | ## ✅ Pros
34 | - Simple to implement and maintain.
35 | - Easy to extend (just add a new queue + routing key for a new channel).
36 | - Centralized control → one exchange for all traffic.
37 | - Decouples producers from consumers (producers don’t need to know queue names).
38 |
39 | ## ❌ Cons
40 | - Low-priority messages will still be delivered, but they can experience significant delays if high-priority traffic continues to dominate the queue.
41 | - Single exchange could become a **bottleneck** under very high traffic.
42 | - Harder to apply **per-channel scaling rules** (all messages pass through the same exchange).
43 |
44 |
45 | ---
46 | ## 🏗️ Type-2: Three Priority Channels (High, Normal, Low)
47 | 
48 | In this design, we maintain **three separate queues and consumers**:
49 |
50 | - `notification.high` → handled by `highConsumer` (higher concurrency).
51 | - `notification.normal` → handled by `normalConsumer`.
52 | - `notification.low` → handled by `lowConsumer` (lower concurrency).
53 |
54 | Each consumer can process **multiple notification types** (Email, SMS, WhatsApp), but the channel determines **priority**.
55 |
56 | ---
57 |
58 |
59 | ## ✨ Features
60 | - Supports **different notification types**: Email, SMS, WhatsApp.
61 | - **Priority-based processing** (High, Normal, Low).
62 | - **Custom concurrency & prefetch** tuning per channel.
63 | - **Dead Letter Queues (DLQ)** for failed messages.
64 | - **Retry mechanism** with requeue/reprocess strategy.
65 | - **Scalable design** → each channel can be scaled independently.
66 |
67 |
68 | ## ✅ Pros
69 | - **Strict priority isolation** → high-priority messages are never blocked by low-priority traffic.
70 | - **Custom concurrency per channel** → e.g., 5 workers for high, 3 for normal, 1 for low.
71 | - **Independent scaling** → scale consumers per channel depending on workload.
72 | - **Per-priority DLQ handling** → easier debugging and retries.
73 | - **Traffic resilience** → a spike in low-priority traffic won’t impact high-priority processing.
74 | - **At-least-once delivery** → thanks to consumer groups and RabbitMQ’s reliability.
75 |
76 | ---
77 |
78 | ## ❌ Cons
79 | - **Infrastructure overhead** → more queues, DLQs, and bindings to manage.
80 | - **Producer responsibility** → the sender must correctly choose the priority channel.
81 | - **Possible resource waste** → high-priority workers may sit idle while low-priority lags.
82 | - **No dynamic reprioritization** → once in `low`, a message can’t jump to `high` without re-publishing.
83 | - **Complex retry logic** → each priority has its own DLQ; retries need coordination.
84 | - **Risk of starvation** → low-priority traffic may be heavily delayed if high-priority is constant.
85 |
86 | ---
87 |
88 |
89 | ## 🔹 Type-3: Separate queues per type + per priority
90 | 
91 |
92 | ## Type 3 Design (9 Queues)
93 |
94 | - **EmailHigh, EmailNormal, EmailLow**
95 | - **SMSHigh, SMSNormal, SMSLow**
96 | - **WhatsAppHigh, WhatsAppNormal, WhatsAppLow**
97 |
98 | ---
99 | **Flow:**
100 | 1. The producer sends a `NotificationRequest` with `type` (email, sms, whatsapp) and `priority` (high, normal, low).
101 | 2. The controller routes the message to the correct queue dynamically.
102 | 3. Each queue has its own consumer with configurable concurrency, retries, and DLQ.
103 | 4. Consumers process messages independently and can be assigned to different service providers per use case.
104 |
105 | ---
106 |
107 |
108 | ## Features
109 |
110 | - Handles **3 types of notifications**: Email, SMS, WhatsApp.
111 | - **3 priority levels** per type: High, Normal, Low.
112 | - **Independent scaling** per queue using concurrency and prefetch settings.
113 | - **Retry and DLQ** per queue for robust error handling.
114 | - **Priority isolation**: high-priority messages are not blocked by lower-priority messages.
115 | - **Supports multiple service providers** per type based on use case (e.g., transactional vs marketing).
116 | - Fully **decoupled producer-consumer** model.
117 |
118 | ---
119 |
120 | ## ✅ Pros
121 |
122 | - **Full priority separation**: High-priority messages are never blocked by lower-priority ones.
123 | - **Independent scaling per queue**: Concurrency and prefetch can be tuned for each type and priority.
124 | - **Per-queue retry and DLQ**: Failures are isolated and easier to monitor.
125 | - **Predictable performance**: Guaranteed processing speed for high-priority queues.
126 | - **Fine-grained control**: Configure retries, TTLs, concurrency, and prefetch per queue.
127 | - **Supports multiple service providers**: Each queue can be handled by a different provider depending on use case.
128 | - **Separation of concerns**: Each consumer handles a specific type + priority, simplifying monitoring and maintenance.
129 |
130 | ---
131 |
132 | ## ❌ Cons
133 |
134 | - **Infrastructure overhead**: 9 queues + 9 DLQs + multiple consumers increase complexity.
135 | - **Producer responsibility**: Must correctly select the type and priority queue.
136 | - **Resource imbalance**: High-priority workers may be idle while low-priority queues are full.
137 | - **Limited dynamic reprioritization**: Low-priority messages cannot automatically jump to high-priority queues.
138 | - **Scaling complexity**: Adding a new type or priority requires creating new queues and consumers.
139 | - **Monitoring complexity**: More queues mean more metrics and DLQs to track.
140 |
141 | ---
142 | ## Notes
143 |
144 | - This project focuses on **architecture design and queue optimization** rather than complete implementation.
145 | - Message types, priorities, concurrency, retries, and DLQs can be adjusted for **real-world use cases**.
146 |
147 |
--------------------------------------------------------------------------------
/.idea/uiDesigner.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 |
7 |
8 | -
9 |
10 |
11 | -
12 |
13 |
14 | -
15 |
16 |
17 | -
18 |
19 |
20 |
21 |
22 |
23 | -
24 |
25 |
26 |
27 |
28 |
29 | -
30 |
31 |
32 |
33 |
34 |
35 | -
36 |
37 |
38 |
39 |
40 |
41 | -
42 |
43 |
44 |
45 |
46 | -
47 |
48 |
49 |
50 |
51 | -
52 |
53 |
54 |
55 |
56 | -
57 |
58 |
59 |
60 |
61 | -
62 |
63 |
64 |
65 |
66 | -
67 |
68 |
69 |
70 |
71 | -
72 |
73 |
74 | -
75 |
76 |
77 |
78 |
79 | -
80 |
81 |
82 |
83 |
84 | -
85 |
86 |
87 |
88 |
89 | -
90 |
91 |
92 |
93 |
94 | -
95 |
96 |
97 |
98 |
99 | -
100 |
101 |
102 | -
103 |
104 |
105 | -
106 |
107 |
108 | -
109 |
110 |
111 | -
112 |
113 |
114 |
115 |
116 | -
117 |
118 |
119 | -
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/notification-type-1/notification-type-1/mvnw.cmd:
--------------------------------------------------------------------------------
1 | <# : batch portion
2 | @REM ----------------------------------------------------------------------------
3 | @REM Licensed to the Apache Software Foundation (ASF) under one
4 | @REM or more contributor license agreements. See the NOTICE file
5 | @REM distributed with this work for additional information
6 | @REM regarding copyright ownership. The ASF licenses this file
7 | @REM to you under the Apache License, Version 2.0 (the
8 | @REM "License"); you may not use this file except in compliance
9 | @REM with the License. You may obtain a copy of the License at
10 | @REM
11 | @REM http://www.apache.org/licenses/LICENSE-2.0
12 | @REM
13 | @REM Unless required by applicable law or agreed to in writing,
14 | @REM software distributed under the License is distributed on an
15 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | @REM KIND, either express or implied. See the License for the
17 | @REM specific language governing permissions and limitations
18 | @REM under the License.
19 | @REM ----------------------------------------------------------------------------
20 |
21 | @REM ----------------------------------------------------------------------------
22 | @REM Apache Maven Wrapper startup batch script, version 3.3.3
23 | @REM
24 | @REM Optional ENV vars
25 | @REM MVNW_REPOURL - repo url base for downloading maven distribution
26 | @REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
27 | @REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
28 | @REM ----------------------------------------------------------------------------
29 |
30 | @IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
31 | @SET __MVNW_CMD__=
32 | @SET __MVNW_ERROR__=
33 | @SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
34 | @SET PSModulePath=
35 | @FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
36 | IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
37 | )
38 | @SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
39 | @SET __MVNW_PSMODULEP_SAVE=
40 | @SET __MVNW_ARG0_NAME__=
41 | @SET MVNW_USERNAME=
42 | @SET MVNW_PASSWORD=
43 | @IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*)
44 | @echo Cannot start maven from wrapper >&2 && exit /b 1
45 | @GOTO :EOF
46 | : end batch / begin powershell #>
47 |
48 | $ErrorActionPreference = "Stop"
49 | if ($env:MVNW_VERBOSE -eq "true") {
50 | $VerbosePreference = "Continue"
51 | }
52 |
53 | # calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
54 | $distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
55 | if (!$distributionUrl) {
56 | Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
57 | }
58 |
59 | switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
60 | "maven-mvnd-*" {
61 | $USE_MVND = $true
62 | $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
63 | $MVN_CMD = "mvnd.cmd"
64 | break
65 | }
66 | default {
67 | $USE_MVND = $false
68 | $MVN_CMD = $script -replace '^mvnw','mvn'
69 | break
70 | }
71 | }
72 |
73 | # apply MVNW_REPOURL and calculate MAVEN_HOME
74 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
75 | if ($env:MVNW_REPOURL) {
76 | $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" }
77 | $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')"
78 | }
79 | $distributionUrlName = $distributionUrl -replace '^.*/',''
80 | $distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
81 |
82 | $MAVEN_M2_PATH = "$HOME/.m2"
83 | if ($env:MAVEN_USER_HOME) {
84 | $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME"
85 | }
86 |
87 | if (-not (Test-Path -Path $MAVEN_M2_PATH)) {
88 | New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null
89 | }
90 |
91 | $MAVEN_WRAPPER_DISTS = $null
92 | if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) {
93 | $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists"
94 | } else {
95 | $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists"
96 | }
97 |
98 | $MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain"
99 | $MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
100 | $MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
101 |
102 | if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
103 | Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
104 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
105 | exit $?
106 | }
107 |
108 | if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
109 | Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
110 | }
111 |
112 | # prepare tmp dir
113 | $TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
114 | $TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
115 | $TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
116 | trap {
117 | if ($TMP_DOWNLOAD_DIR.Exists) {
118 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
119 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
120 | }
121 | }
122 |
123 | New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
124 |
125 | # Download and Install Apache Maven
126 | Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
127 | Write-Verbose "Downloading from: $distributionUrl"
128 | Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
129 |
130 | $webclient = New-Object System.Net.WebClient
131 | if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
132 | $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
133 | }
134 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
135 | $webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
136 |
137 | # If specified, validate the SHA-256 sum of the Maven distribution zip file
138 | $distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
139 | if ($distributionSha256Sum) {
140 | if ($USE_MVND) {
141 | Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
142 | }
143 | Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
144 | if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
145 | Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
146 | }
147 | }
148 |
149 | # unzip and move
150 | Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
151 |
152 | # Find the actual extracted directory name (handles snapshots where filename != directory name)
153 | $actualDistributionDir = ""
154 |
155 | # First try the expected directory name (for regular distributions)
156 | $expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain"
157 | $expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD"
158 | if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) {
159 | $actualDistributionDir = $distributionUrlNameMain
160 | }
161 |
162 | # If not found, search for any directory with the Maven executable (for snapshots)
163 | if (!$actualDistributionDir) {
164 | Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object {
165 | $testPath = Join-Path $_.FullName "bin/$MVN_CMD"
166 | if (Test-Path -Path $testPath -PathType Leaf) {
167 | $actualDistributionDir = $_.Name
168 | }
169 | }
170 | }
171 |
172 | if (!$actualDistributionDir) {
173 | Write-Error "Could not find Maven distribution directory in extracted archive"
174 | }
175 |
176 | Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir"
177 | Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null
178 | try {
179 | Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
180 | } catch {
181 | if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
182 | Write-Error "fail to move MAVEN_HOME"
183 | }
184 | } finally {
185 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
186 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
187 | }
188 |
189 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
190 |
--------------------------------------------------------------------------------
/notification-type-2/notification-type-2/mvnw.cmd:
--------------------------------------------------------------------------------
1 | <# : batch portion
2 | @REM ----------------------------------------------------------------------------
3 | @REM Licensed to the Apache Software Foundation (ASF) under one
4 | @REM or more contributor license agreements. See the NOTICE file
5 | @REM distributed with this work for additional information
6 | @REM regarding copyright ownership. The ASF licenses this file
7 | @REM to you under the Apache License, Version 2.0 (the
8 | @REM "License"); you may not use this file except in compliance
9 | @REM with the License. You may obtain a copy of the License at
10 | @REM
11 | @REM http://www.apache.org/licenses/LICENSE-2.0
12 | @REM
13 | @REM Unless required by applicable law or agreed to in writing,
14 | @REM software distributed under the License is distributed on an
15 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | @REM KIND, either express or implied. See the License for the
17 | @REM specific language governing permissions and limitations
18 | @REM under the License.
19 | @REM ----------------------------------------------------------------------------
20 |
21 | @REM ----------------------------------------------------------------------------
22 | @REM Apache Maven Wrapper startup batch script, version 3.3.3
23 | @REM
24 | @REM Optional ENV vars
25 | @REM MVNW_REPOURL - repo url base for downloading maven distribution
26 | @REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
27 | @REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
28 | @REM ----------------------------------------------------------------------------
29 |
30 | @IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
31 | @SET __MVNW_CMD__=
32 | @SET __MVNW_ERROR__=
33 | @SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
34 | @SET PSModulePath=
35 | @FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
36 | IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
37 | )
38 | @SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
39 | @SET __MVNW_PSMODULEP_SAVE=
40 | @SET __MVNW_ARG0_NAME__=
41 | @SET MVNW_USERNAME=
42 | @SET MVNW_PASSWORD=
43 | @IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*)
44 | @echo Cannot start maven from wrapper >&2 && exit /b 1
45 | @GOTO :EOF
46 | : end batch / begin powershell #>
47 |
48 | $ErrorActionPreference = "Stop"
49 | if ($env:MVNW_VERBOSE -eq "true") {
50 | $VerbosePreference = "Continue"
51 | }
52 |
53 | # calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
54 | $distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
55 | if (!$distributionUrl) {
56 | Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
57 | }
58 |
59 | switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
60 | "maven-mvnd-*" {
61 | $USE_MVND = $true
62 | $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
63 | $MVN_CMD = "mvnd.cmd"
64 | break
65 | }
66 | default {
67 | $USE_MVND = $false
68 | $MVN_CMD = $script -replace '^mvnw','mvn'
69 | break
70 | }
71 | }
72 |
73 | # apply MVNW_REPOURL and calculate MAVEN_HOME
74 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
75 | if ($env:MVNW_REPOURL) {
76 | $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" }
77 | $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')"
78 | }
79 | $distributionUrlName = $distributionUrl -replace '^.*/',''
80 | $distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
81 |
82 | $MAVEN_M2_PATH = "$HOME/.m2"
83 | if ($env:MAVEN_USER_HOME) {
84 | $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME"
85 | }
86 |
87 | if (-not (Test-Path -Path $MAVEN_M2_PATH)) {
88 | New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null
89 | }
90 |
91 | $MAVEN_WRAPPER_DISTS = $null
92 | if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) {
93 | $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists"
94 | } else {
95 | $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists"
96 | }
97 |
98 | $MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain"
99 | $MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
100 | $MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
101 |
102 | if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
103 | Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
104 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
105 | exit $?
106 | }
107 |
108 | if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
109 | Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
110 | }
111 |
112 | # prepare tmp dir
113 | $TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
114 | $TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
115 | $TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
116 | trap {
117 | if ($TMP_DOWNLOAD_DIR.Exists) {
118 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
119 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
120 | }
121 | }
122 |
123 | New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
124 |
125 | # Download and Install Apache Maven
126 | Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
127 | Write-Verbose "Downloading from: $distributionUrl"
128 | Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
129 |
130 | $webclient = New-Object System.Net.WebClient
131 | if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
132 | $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
133 | }
134 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
135 | $webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
136 |
137 | # If specified, validate the SHA-256 sum of the Maven distribution zip file
138 | $distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
139 | if ($distributionSha256Sum) {
140 | if ($USE_MVND) {
141 | Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
142 | }
143 | Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
144 | if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
145 | Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
146 | }
147 | }
148 |
149 | # unzip and move
150 | Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
151 |
152 | # Find the actual extracted directory name (handles snapshots where filename != directory name)
153 | $actualDistributionDir = ""
154 |
155 | # First try the expected directory name (for regular distributions)
156 | $expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain"
157 | $expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD"
158 | if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) {
159 | $actualDistributionDir = $distributionUrlNameMain
160 | }
161 |
162 | # If not found, search for any directory with the Maven executable (for snapshots)
163 | if (!$actualDistributionDir) {
164 | Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object {
165 | $testPath = Join-Path $_.FullName "bin/$MVN_CMD"
166 | if (Test-Path -Path $testPath -PathType Leaf) {
167 | $actualDistributionDir = $_.Name
168 | }
169 | }
170 | }
171 |
172 | if (!$actualDistributionDir) {
173 | Write-Error "Could not find Maven distribution directory in extracted archive"
174 | }
175 |
176 | Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir"
177 | Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null
178 | try {
179 | Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
180 | } catch {
181 | if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
182 | Write-Error "fail to move MAVEN_HOME"
183 | }
184 | } finally {
185 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
186 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
187 | }
188 |
189 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
190 |
--------------------------------------------------------------------------------
/notification-type-3/notification-type-3/mvnw.cmd:
--------------------------------------------------------------------------------
1 | <# : batch portion
2 | @REM ----------------------------------------------------------------------------
3 | @REM Licensed to the Apache Software Foundation (ASF) under one
4 | @REM or more contributor license agreements. See the NOTICE file
5 | @REM distributed with this work for additional information
6 | @REM regarding copyright ownership. The ASF licenses this file
7 | @REM to you under the Apache License, Version 2.0 (the
8 | @REM "License"); you may not use this file except in compliance
9 | @REM with the License. You may obtain a copy of the License at
10 | @REM
11 | @REM http://www.apache.org/licenses/LICENSE-2.0
12 | @REM
13 | @REM Unless required by applicable law or agreed to in writing,
14 | @REM software distributed under the License is distributed on an
15 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | @REM KIND, either express or implied. See the License for the
17 | @REM specific language governing permissions and limitations
18 | @REM under the License.
19 | @REM ----------------------------------------------------------------------------
20 |
21 | @REM ----------------------------------------------------------------------------
22 | @REM Apache Maven Wrapper startup batch script, version 3.3.3
23 | @REM
24 | @REM Optional ENV vars
25 | @REM MVNW_REPOURL - repo url base for downloading maven distribution
26 | @REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
27 | @REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
28 | @REM ----------------------------------------------------------------------------
29 |
30 | @IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
31 | @SET __MVNW_CMD__=
32 | @SET __MVNW_ERROR__=
33 | @SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
34 | @SET PSModulePath=
35 | @FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
36 | IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
37 | )
38 | @SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
39 | @SET __MVNW_PSMODULEP_SAVE=
40 | @SET __MVNW_ARG0_NAME__=
41 | @SET MVNW_USERNAME=
42 | @SET MVNW_PASSWORD=
43 | @IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*)
44 | @echo Cannot start maven from wrapper >&2 && exit /b 1
45 | @GOTO :EOF
46 | : end batch / begin powershell #>
47 |
48 | $ErrorActionPreference = "Stop"
49 | if ($env:MVNW_VERBOSE -eq "true") {
50 | $VerbosePreference = "Continue"
51 | }
52 |
53 | # calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
54 | $distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
55 | if (!$distributionUrl) {
56 | Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
57 | }
58 |
59 | switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
60 | "maven-mvnd-*" {
61 | $USE_MVND = $true
62 | $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
63 | $MVN_CMD = "mvnd.cmd"
64 | break
65 | }
66 | default {
67 | $USE_MVND = $false
68 | $MVN_CMD = $script -replace '^mvnw','mvn'
69 | break
70 | }
71 | }
72 |
73 | # apply MVNW_REPOURL and calculate MAVEN_HOME
74 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
75 | if ($env:MVNW_REPOURL) {
76 | $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" }
77 | $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')"
78 | }
79 | $distributionUrlName = $distributionUrl -replace '^.*/',''
80 | $distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
81 |
82 | $MAVEN_M2_PATH = "$HOME/.m2"
83 | if ($env:MAVEN_USER_HOME) {
84 | $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME"
85 | }
86 |
87 | if (-not (Test-Path -Path $MAVEN_M2_PATH)) {
88 | New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null
89 | }
90 |
91 | $MAVEN_WRAPPER_DISTS = $null
92 | if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) {
93 | $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists"
94 | } else {
95 | $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists"
96 | }
97 |
98 | $MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain"
99 | $MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
100 | $MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
101 |
102 | if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
103 | Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
104 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
105 | exit $?
106 | }
107 |
108 | if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
109 | Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
110 | }
111 |
112 | # prepare tmp dir
113 | $TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
114 | $TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
115 | $TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
116 | trap {
117 | if ($TMP_DOWNLOAD_DIR.Exists) {
118 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
119 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
120 | }
121 | }
122 |
123 | New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
124 |
125 | # Download and Install Apache Maven
126 | Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
127 | Write-Verbose "Downloading from: $distributionUrl"
128 | Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
129 |
130 | $webclient = New-Object System.Net.WebClient
131 | if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
132 | $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
133 | }
134 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
135 | $webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
136 |
137 | # If specified, validate the SHA-256 sum of the Maven distribution zip file
138 | $distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
139 | if ($distributionSha256Sum) {
140 | if ($USE_MVND) {
141 | Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
142 | }
143 | Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
144 | if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
145 | Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
146 | }
147 | }
148 |
149 | # unzip and move
150 | Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
151 |
152 | # Find the actual extracted directory name (handles snapshots where filename != directory name)
153 | $actualDistributionDir = ""
154 |
155 | # First try the expected directory name (for regular distributions)
156 | $expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain"
157 | $expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD"
158 | if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) {
159 | $actualDistributionDir = $distributionUrlNameMain
160 | }
161 |
162 | # If not found, search for any directory with the Maven executable (for snapshots)
163 | if (!$actualDistributionDir) {
164 | Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object {
165 | $testPath = Join-Path $_.FullName "bin/$MVN_CMD"
166 | if (Test-Path -Path $testPath -PathType Leaf) {
167 | $actualDistributionDir = $_.Name
168 | }
169 | }
170 | }
171 |
172 | if (!$actualDistributionDir) {
173 | Write-Error "Could not find Maven distribution directory in extracted archive"
174 | }
175 |
176 | Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir"
177 | Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null
178 | try {
179 | Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
180 | } catch {
181 | if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
182 | Write-Error "fail to move MAVEN_HOME"
183 | }
184 | } finally {
185 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
186 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
187 | }
188 |
189 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
190 |
--------------------------------------------------------------------------------
/notification-type-1/notification-type-1/mvnw:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # ----------------------------------------------------------------------------
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | # ----------------------------------------------------------------------------
20 |
21 | # ----------------------------------------------------------------------------
22 | # Apache Maven Wrapper startup batch script, version 3.3.3
23 | #
24 | # Optional ENV vars
25 | # -----------------
26 | # JAVA_HOME - location of a JDK home dir, required when download maven via java source
27 | # MVNW_REPOURL - repo url base for downloading maven distribution
28 | # MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
29 | # MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
30 | # ----------------------------------------------------------------------------
31 |
32 | set -euf
33 | [ "${MVNW_VERBOSE-}" != debug ] || set -x
34 |
35 | # OS specific support.
36 | native_path() { printf %s\\n "$1"; }
37 | case "$(uname)" in
38 | CYGWIN* | MINGW*)
39 | [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
40 | native_path() { cygpath --path --windows "$1"; }
41 | ;;
42 | esac
43 |
44 | # set JAVACMD and JAVACCMD
45 | set_java_home() {
46 | # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
47 | if [ -n "${JAVA_HOME-}" ]; then
48 | if [ -x "$JAVA_HOME/jre/sh/java" ]; then
49 | # IBM's JDK on AIX uses strange locations for the executables
50 | JAVACMD="$JAVA_HOME/jre/sh/java"
51 | JAVACCMD="$JAVA_HOME/jre/sh/javac"
52 | else
53 | JAVACMD="$JAVA_HOME/bin/java"
54 | JAVACCMD="$JAVA_HOME/bin/javac"
55 |
56 | if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
57 | echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
58 | echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
59 | return 1
60 | fi
61 | fi
62 | else
63 | JAVACMD="$(
64 | 'set' +e
65 | 'unset' -f command 2>/dev/null
66 | 'command' -v java
67 | )" || :
68 | JAVACCMD="$(
69 | 'set' +e
70 | 'unset' -f command 2>/dev/null
71 | 'command' -v javac
72 | )" || :
73 |
74 | if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
75 | echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
76 | return 1
77 | fi
78 | fi
79 | }
80 |
81 | # hash string like Java String::hashCode
82 | hash_string() {
83 | str="${1:-}" h=0
84 | while [ -n "$str" ]; do
85 | char="${str%"${str#?}"}"
86 | h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
87 | str="${str#?}"
88 | done
89 | printf %x\\n $h
90 | }
91 |
92 | verbose() { :; }
93 | [ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
94 |
95 | die() {
96 | printf %s\\n "$1" >&2
97 | exit 1
98 | }
99 |
100 | trim() {
101 | # MWRAPPER-139:
102 | # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
103 | # Needed for removing poorly interpreted newline sequences when running in more
104 | # exotic environments such as mingw bash on Windows.
105 | printf "%s" "${1}" | tr -d '[:space:]'
106 | }
107 |
108 | scriptDir="$(dirname "$0")"
109 | scriptName="$(basename "$0")"
110 |
111 | # parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
112 | while IFS="=" read -r key value; do
113 | case "${key-}" in
114 | distributionUrl) distributionUrl=$(trim "${value-}") ;;
115 | distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
116 | esac
117 | done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties"
118 | [ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
119 |
120 | case "${distributionUrl##*/}" in
121 | maven-mvnd-*bin.*)
122 | MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
123 | case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
124 | *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
125 | :Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
126 | :Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
127 | :Linux*x86_64*) distributionPlatform=linux-amd64 ;;
128 | *)
129 | echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
130 | distributionPlatform=linux-amd64
131 | ;;
132 | esac
133 | distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
134 | ;;
135 | maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
136 | *) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
137 | esac
138 |
139 | # apply MVNW_REPOURL and calculate MAVEN_HOME
140 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
141 | [ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
142 | distributionUrlName="${distributionUrl##*/}"
143 | distributionUrlNameMain="${distributionUrlName%.*}"
144 | distributionUrlNameMain="${distributionUrlNameMain%-bin}"
145 | MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
146 | MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
147 |
148 | exec_maven() {
149 | unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
150 | exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
151 | }
152 |
153 | if [ -d "$MAVEN_HOME" ]; then
154 | verbose "found existing MAVEN_HOME at $MAVEN_HOME"
155 | exec_maven "$@"
156 | fi
157 |
158 | case "${distributionUrl-}" in
159 | *?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
160 | *) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
161 | esac
162 |
163 | # prepare tmp dir
164 | if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
165 | clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
166 | trap clean HUP INT TERM EXIT
167 | else
168 | die "cannot create temp dir"
169 | fi
170 |
171 | mkdir -p -- "${MAVEN_HOME%/*}"
172 |
173 | # Download and Install Apache Maven
174 | verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
175 | verbose "Downloading from: $distributionUrl"
176 | verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
177 |
178 | # select .zip or .tar.gz
179 | if ! command -v unzip >/dev/null; then
180 | distributionUrl="${distributionUrl%.zip}.tar.gz"
181 | distributionUrlName="${distributionUrl##*/}"
182 | fi
183 |
184 | # verbose opt
185 | __MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
186 | [ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
187 |
188 | # normalize http auth
189 | case "${MVNW_PASSWORD:+has-password}" in
190 | '') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
191 | has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
192 | esac
193 |
194 | if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
195 | verbose "Found wget ... using wget"
196 | wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
197 | elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
198 | verbose "Found curl ... using curl"
199 | curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
200 | elif set_java_home; then
201 | verbose "Falling back to use Java to download"
202 | javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
203 | targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
204 | cat >"$javaSource" <<-END
205 | public class Downloader extends java.net.Authenticator
206 | {
207 | protected java.net.PasswordAuthentication getPasswordAuthentication()
208 | {
209 | return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
210 | }
211 | public static void main( String[] args ) throws Exception
212 | {
213 | setDefault( new Downloader() );
214 | java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
215 | }
216 | }
217 | END
218 | # For Cygwin/MinGW, switch paths to Windows format before running javac and java
219 | verbose " - Compiling Downloader.java ..."
220 | "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
221 | verbose " - Running Downloader.java ..."
222 | "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
223 | fi
224 |
225 | # If specified, validate the SHA-256 sum of the Maven distribution zip file
226 | if [ -n "${distributionSha256Sum-}" ]; then
227 | distributionSha256Result=false
228 | if [ "$MVN_CMD" = mvnd.sh ]; then
229 | echo "Checksum validation is not supported for maven-mvnd." >&2
230 | echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
231 | exit 1
232 | elif command -v sha256sum >/dev/null; then
233 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then
234 | distributionSha256Result=true
235 | fi
236 | elif command -v shasum >/dev/null; then
237 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
238 | distributionSha256Result=true
239 | fi
240 | else
241 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
242 | echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
243 | exit 1
244 | fi
245 | if [ $distributionSha256Result = false ]; then
246 | echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
247 | echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
248 | exit 1
249 | fi
250 | fi
251 |
252 | # unzip and move
253 | if command -v unzip >/dev/null; then
254 | unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
255 | else
256 | tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
257 | fi
258 |
259 | # Find the actual extracted directory name (handles snapshots where filename != directory name)
260 | actualDistributionDir=""
261 |
262 | # First try the expected directory name (for regular distributions)
263 | if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then
264 | if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then
265 | actualDistributionDir="$distributionUrlNameMain"
266 | fi
267 | fi
268 |
269 | # If not found, search for any directory with the Maven executable (for snapshots)
270 | if [ -z "$actualDistributionDir" ]; then
271 | # enable globbing to iterate over items
272 | set +f
273 | for dir in "$TMP_DOWNLOAD_DIR"/*; do
274 | if [ -d "$dir" ]; then
275 | if [ -f "$dir/bin/$MVN_CMD" ]; then
276 | actualDistributionDir="$(basename "$dir")"
277 | break
278 | fi
279 | fi
280 | done
281 | set -f
282 | fi
283 |
284 | if [ -z "$actualDistributionDir" ]; then
285 | verbose "Contents of $TMP_DOWNLOAD_DIR:"
286 | verbose "$(ls -la "$TMP_DOWNLOAD_DIR")"
287 | die "Could not find Maven distribution directory in extracted archive"
288 | fi
289 |
290 | verbose "Found extracted Maven distribution directory: $actualDistributionDir"
291 | printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url"
292 | mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
293 |
294 | clean || :
295 | exec_maven "$@"
296 |
--------------------------------------------------------------------------------
/notification-type-2/notification-type-2/mvnw:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # ----------------------------------------------------------------------------
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | # ----------------------------------------------------------------------------
20 |
21 | # ----------------------------------------------------------------------------
22 | # Apache Maven Wrapper startup batch script, version 3.3.3
23 | #
24 | # Optional ENV vars
25 | # -----------------
26 | # JAVA_HOME - location of a JDK home dir, required when download maven via java source
27 | # MVNW_REPOURL - repo url base for downloading maven distribution
28 | # MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
29 | # MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
30 | # ----------------------------------------------------------------------------
31 |
32 | set -euf
33 | [ "${MVNW_VERBOSE-}" != debug ] || set -x
34 |
35 | # OS specific support.
36 | native_path() { printf %s\\n "$1"; }
37 | case "$(uname)" in
38 | CYGWIN* | MINGW*)
39 | [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
40 | native_path() { cygpath --path --windows "$1"; }
41 | ;;
42 | esac
43 |
44 | # set JAVACMD and JAVACCMD
45 | set_java_home() {
46 | # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
47 | if [ -n "${JAVA_HOME-}" ]; then
48 | if [ -x "$JAVA_HOME/jre/sh/java" ]; then
49 | # IBM's JDK on AIX uses strange locations for the executables
50 | JAVACMD="$JAVA_HOME/jre/sh/java"
51 | JAVACCMD="$JAVA_HOME/jre/sh/javac"
52 | else
53 | JAVACMD="$JAVA_HOME/bin/java"
54 | JAVACCMD="$JAVA_HOME/bin/javac"
55 |
56 | if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
57 | echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
58 | echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
59 | return 1
60 | fi
61 | fi
62 | else
63 | JAVACMD="$(
64 | 'set' +e
65 | 'unset' -f command 2>/dev/null
66 | 'command' -v java
67 | )" || :
68 | JAVACCMD="$(
69 | 'set' +e
70 | 'unset' -f command 2>/dev/null
71 | 'command' -v javac
72 | )" || :
73 |
74 | if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
75 | echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
76 | return 1
77 | fi
78 | fi
79 | }
80 |
81 | # hash string like Java String::hashCode
82 | hash_string() {
83 | str="${1:-}" h=0
84 | while [ -n "$str" ]; do
85 | char="${str%"${str#?}"}"
86 | h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
87 | str="${str#?}"
88 | done
89 | printf %x\\n $h
90 | }
91 |
92 | verbose() { :; }
93 | [ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
94 |
95 | die() {
96 | printf %s\\n "$1" >&2
97 | exit 1
98 | }
99 |
100 | trim() {
101 | # MWRAPPER-139:
102 | # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
103 | # Needed for removing poorly interpreted newline sequences when running in more
104 | # exotic environments such as mingw bash on Windows.
105 | printf "%s" "${1}" | tr -d '[:space:]'
106 | }
107 |
108 | scriptDir="$(dirname "$0")"
109 | scriptName="$(basename "$0")"
110 |
111 | # parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
112 | while IFS="=" read -r key value; do
113 | case "${key-}" in
114 | distributionUrl) distributionUrl=$(trim "${value-}") ;;
115 | distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
116 | esac
117 | done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties"
118 | [ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
119 |
120 | case "${distributionUrl##*/}" in
121 | maven-mvnd-*bin.*)
122 | MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
123 | case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
124 | *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
125 | :Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
126 | :Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
127 | :Linux*x86_64*) distributionPlatform=linux-amd64 ;;
128 | *)
129 | echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
130 | distributionPlatform=linux-amd64
131 | ;;
132 | esac
133 | distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
134 | ;;
135 | maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
136 | *) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
137 | esac
138 |
139 | # apply MVNW_REPOURL and calculate MAVEN_HOME
140 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
141 | [ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
142 | distributionUrlName="${distributionUrl##*/}"
143 | distributionUrlNameMain="${distributionUrlName%.*}"
144 | distributionUrlNameMain="${distributionUrlNameMain%-bin}"
145 | MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
146 | MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
147 |
148 | exec_maven() {
149 | unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
150 | exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
151 | }
152 |
153 | if [ -d "$MAVEN_HOME" ]; then
154 | verbose "found existing MAVEN_HOME at $MAVEN_HOME"
155 | exec_maven "$@"
156 | fi
157 |
158 | case "${distributionUrl-}" in
159 | *?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
160 | *) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
161 | esac
162 |
163 | # prepare tmp dir
164 | if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
165 | clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
166 | trap clean HUP INT TERM EXIT
167 | else
168 | die "cannot create temp dir"
169 | fi
170 |
171 | mkdir -p -- "${MAVEN_HOME%/*}"
172 |
173 | # Download and Install Apache Maven
174 | verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
175 | verbose "Downloading from: $distributionUrl"
176 | verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
177 |
178 | # select .zip or .tar.gz
179 | if ! command -v unzip >/dev/null; then
180 | distributionUrl="${distributionUrl%.zip}.tar.gz"
181 | distributionUrlName="${distributionUrl##*/}"
182 | fi
183 |
184 | # verbose opt
185 | __MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
186 | [ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
187 |
188 | # normalize http auth
189 | case "${MVNW_PASSWORD:+has-password}" in
190 | '') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
191 | has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
192 | esac
193 |
194 | if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
195 | verbose "Found wget ... using wget"
196 | wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
197 | elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
198 | verbose "Found curl ... using curl"
199 | curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
200 | elif set_java_home; then
201 | verbose "Falling back to use Java to download"
202 | javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
203 | targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
204 | cat >"$javaSource" <<-END
205 | public class Downloader extends java.net.Authenticator
206 | {
207 | protected java.net.PasswordAuthentication getPasswordAuthentication()
208 | {
209 | return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
210 | }
211 | public static void main( String[] args ) throws Exception
212 | {
213 | setDefault( new Downloader() );
214 | java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
215 | }
216 | }
217 | END
218 | # For Cygwin/MinGW, switch paths to Windows format before running javac and java
219 | verbose " - Compiling Downloader.java ..."
220 | "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
221 | verbose " - Running Downloader.java ..."
222 | "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
223 | fi
224 |
225 | # If specified, validate the SHA-256 sum of the Maven distribution zip file
226 | if [ -n "${distributionSha256Sum-}" ]; then
227 | distributionSha256Result=false
228 | if [ "$MVN_CMD" = mvnd.sh ]; then
229 | echo "Checksum validation is not supported for maven-mvnd." >&2
230 | echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
231 | exit 1
232 | elif command -v sha256sum >/dev/null; then
233 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then
234 | distributionSha256Result=true
235 | fi
236 | elif command -v shasum >/dev/null; then
237 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
238 | distributionSha256Result=true
239 | fi
240 | else
241 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
242 | echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
243 | exit 1
244 | fi
245 | if [ $distributionSha256Result = false ]; then
246 | echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
247 | echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
248 | exit 1
249 | fi
250 | fi
251 |
252 | # unzip and move
253 | if command -v unzip >/dev/null; then
254 | unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
255 | else
256 | tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
257 | fi
258 |
259 | # Find the actual extracted directory name (handles snapshots where filename != directory name)
260 | actualDistributionDir=""
261 |
262 | # First try the expected directory name (for regular distributions)
263 | if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then
264 | if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then
265 | actualDistributionDir="$distributionUrlNameMain"
266 | fi
267 | fi
268 |
269 | # If not found, search for any directory with the Maven executable (for snapshots)
270 | if [ -z "$actualDistributionDir" ]; then
271 | # enable globbing to iterate over items
272 | set +f
273 | for dir in "$TMP_DOWNLOAD_DIR"/*; do
274 | if [ -d "$dir" ]; then
275 | if [ -f "$dir/bin/$MVN_CMD" ]; then
276 | actualDistributionDir="$(basename "$dir")"
277 | break
278 | fi
279 | fi
280 | done
281 | set -f
282 | fi
283 |
284 | if [ -z "$actualDistributionDir" ]; then
285 | verbose "Contents of $TMP_DOWNLOAD_DIR:"
286 | verbose "$(ls -la "$TMP_DOWNLOAD_DIR")"
287 | die "Could not find Maven distribution directory in extracted archive"
288 | fi
289 |
290 | verbose "Found extracted Maven distribution directory: $actualDistributionDir"
291 | printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url"
292 | mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
293 |
294 | clean || :
295 | exec_maven "$@"
296 |
--------------------------------------------------------------------------------
/notification-type-3/notification-type-3/mvnw:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # ----------------------------------------------------------------------------
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | # ----------------------------------------------------------------------------
20 |
21 | # ----------------------------------------------------------------------------
22 | # Apache Maven Wrapper startup batch script, version 3.3.3
23 | #
24 | # Optional ENV vars
25 | # -----------------
26 | # JAVA_HOME - location of a JDK home dir, required when download maven via java source
27 | # MVNW_REPOURL - repo url base for downloading maven distribution
28 | # MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
29 | # MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
30 | # ----------------------------------------------------------------------------
31 |
32 | set -euf
33 | [ "${MVNW_VERBOSE-}" != debug ] || set -x
34 |
35 | # OS specific support.
36 | native_path() { printf %s\\n "$1"; }
37 | case "$(uname)" in
38 | CYGWIN* | MINGW*)
39 | [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
40 | native_path() { cygpath --path --windows "$1"; }
41 | ;;
42 | esac
43 |
44 | # set JAVACMD and JAVACCMD
45 | set_java_home() {
46 | # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
47 | if [ -n "${JAVA_HOME-}" ]; then
48 | if [ -x "$JAVA_HOME/jre/sh/java" ]; then
49 | # IBM's JDK on AIX uses strange locations for the executables
50 | JAVACMD="$JAVA_HOME/jre/sh/java"
51 | JAVACCMD="$JAVA_HOME/jre/sh/javac"
52 | else
53 | JAVACMD="$JAVA_HOME/bin/java"
54 | JAVACCMD="$JAVA_HOME/bin/javac"
55 |
56 | if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
57 | echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
58 | echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
59 | return 1
60 | fi
61 | fi
62 | else
63 | JAVACMD="$(
64 | 'set' +e
65 | 'unset' -f command 2>/dev/null
66 | 'command' -v java
67 | )" || :
68 | JAVACCMD="$(
69 | 'set' +e
70 | 'unset' -f command 2>/dev/null
71 | 'command' -v javac
72 | )" || :
73 |
74 | if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
75 | echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
76 | return 1
77 | fi
78 | fi
79 | }
80 |
81 | # hash string like Java String::hashCode
82 | hash_string() {
83 | str="${1:-}" h=0
84 | while [ -n "$str" ]; do
85 | char="${str%"${str#?}"}"
86 | h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
87 | str="${str#?}"
88 | done
89 | printf %x\\n $h
90 | }
91 |
92 | verbose() { :; }
93 | [ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
94 |
95 | die() {
96 | printf %s\\n "$1" >&2
97 | exit 1
98 | }
99 |
100 | trim() {
101 | # MWRAPPER-139:
102 | # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
103 | # Needed for removing poorly interpreted newline sequences when running in more
104 | # exotic environments such as mingw bash on Windows.
105 | printf "%s" "${1}" | tr -d '[:space:]'
106 | }
107 |
108 | scriptDir="$(dirname "$0")"
109 | scriptName="$(basename "$0")"
110 |
111 | # parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
112 | while IFS="=" read -r key value; do
113 | case "${key-}" in
114 | distributionUrl) distributionUrl=$(trim "${value-}") ;;
115 | distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
116 | esac
117 | done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties"
118 | [ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
119 |
120 | case "${distributionUrl##*/}" in
121 | maven-mvnd-*bin.*)
122 | MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
123 | case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
124 | *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
125 | :Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
126 | :Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
127 | :Linux*x86_64*) distributionPlatform=linux-amd64 ;;
128 | *)
129 | echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
130 | distributionPlatform=linux-amd64
131 | ;;
132 | esac
133 | distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
134 | ;;
135 | maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
136 | *) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
137 | esac
138 |
139 | # apply MVNW_REPOURL and calculate MAVEN_HOME
140 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
141 | [ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
142 | distributionUrlName="${distributionUrl##*/}"
143 | distributionUrlNameMain="${distributionUrlName%.*}"
144 | distributionUrlNameMain="${distributionUrlNameMain%-bin}"
145 | MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
146 | MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
147 |
148 | exec_maven() {
149 | unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
150 | exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
151 | }
152 |
153 | if [ -d "$MAVEN_HOME" ]; then
154 | verbose "found existing MAVEN_HOME at $MAVEN_HOME"
155 | exec_maven "$@"
156 | fi
157 |
158 | case "${distributionUrl-}" in
159 | *?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
160 | *) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
161 | esac
162 |
163 | # prepare tmp dir
164 | if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
165 | clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
166 | trap clean HUP INT TERM EXIT
167 | else
168 | die "cannot create temp dir"
169 | fi
170 |
171 | mkdir -p -- "${MAVEN_HOME%/*}"
172 |
173 | # Download and Install Apache Maven
174 | verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
175 | verbose "Downloading from: $distributionUrl"
176 | verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
177 |
178 | # select .zip or .tar.gz
179 | if ! command -v unzip >/dev/null; then
180 | distributionUrl="${distributionUrl%.zip}.tar.gz"
181 | distributionUrlName="${distributionUrl##*/}"
182 | fi
183 |
184 | # verbose opt
185 | __MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
186 | [ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
187 |
188 | # normalize http auth
189 | case "${MVNW_PASSWORD:+has-password}" in
190 | '') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
191 | has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
192 | esac
193 |
194 | if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
195 | verbose "Found wget ... using wget"
196 | wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
197 | elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
198 | verbose "Found curl ... using curl"
199 | curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
200 | elif set_java_home; then
201 | verbose "Falling back to use Java to download"
202 | javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
203 | targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
204 | cat >"$javaSource" <<-END
205 | public class Downloader extends java.net.Authenticator
206 | {
207 | protected java.net.PasswordAuthentication getPasswordAuthentication()
208 | {
209 | return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
210 | }
211 | public static void main( String[] args ) throws Exception
212 | {
213 | setDefault( new Downloader() );
214 | java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
215 | }
216 | }
217 | END
218 | # For Cygwin/MinGW, switch paths to Windows format before running javac and java
219 | verbose " - Compiling Downloader.java ..."
220 | "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
221 | verbose " - Running Downloader.java ..."
222 | "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
223 | fi
224 |
225 | # If specified, validate the SHA-256 sum of the Maven distribution zip file
226 | if [ -n "${distributionSha256Sum-}" ]; then
227 | distributionSha256Result=false
228 | if [ "$MVN_CMD" = mvnd.sh ]; then
229 | echo "Checksum validation is not supported for maven-mvnd." >&2
230 | echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
231 | exit 1
232 | elif command -v sha256sum >/dev/null; then
233 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then
234 | distributionSha256Result=true
235 | fi
236 | elif command -v shasum >/dev/null; then
237 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
238 | distributionSha256Result=true
239 | fi
240 | else
241 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
242 | echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
243 | exit 1
244 | fi
245 | if [ $distributionSha256Result = false ]; then
246 | echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
247 | echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
248 | exit 1
249 | fi
250 | fi
251 |
252 | # unzip and move
253 | if command -v unzip >/dev/null; then
254 | unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
255 | else
256 | tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
257 | fi
258 |
259 | # Find the actual extracted directory name (handles snapshots where filename != directory name)
260 | actualDistributionDir=""
261 |
262 | # First try the expected directory name (for regular distributions)
263 | if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then
264 | if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then
265 | actualDistributionDir="$distributionUrlNameMain"
266 | fi
267 | fi
268 |
269 | # If not found, search for any directory with the Maven executable (for snapshots)
270 | if [ -z "$actualDistributionDir" ]; then
271 | # enable globbing to iterate over items
272 | set +f
273 | for dir in "$TMP_DOWNLOAD_DIR"/*; do
274 | if [ -d "$dir" ]; then
275 | if [ -f "$dir/bin/$MVN_CMD" ]; then
276 | actualDistributionDir="$(basename "$dir")"
277 | break
278 | fi
279 | fi
280 | done
281 | set -f
282 | fi
283 |
284 | if [ -z "$actualDistributionDir" ]; then
285 | verbose "Contents of $TMP_DOWNLOAD_DIR:"
286 | verbose "$(ls -la "$TMP_DOWNLOAD_DIR")"
287 | die "Could not find Maven distribution directory in extracted archive"
288 | fi
289 |
290 | verbose "Found extracted Maven distribution directory: $actualDistributionDir"
291 | printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url"
292 | mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
293 |
294 | clean || :
295 | exec_maven "$@"
296 |
--------------------------------------------------------------------------------