├── .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 | 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 | 9 | 10 | 14 | 15 | 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 | 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 | ![Type-1 Architecture](./assets/type-1.png) 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 | ![Type-2 Architecture](./assets/type2.png) 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 | ![Type-3 Architecture](./assets/type3.png) 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 | --------------------------------------------------------------------------------